<template>
  <transition name="chat-panel-slide">
    <div 
      v-if="chatStore.visible" 
      class="chat-panel" 
      @click="handlePanelClick"
      :style="panelStyle"
      ref="panelRef"
    >
      <header class="chat-panel__header" @mousedown="handleHeaderMouseDown">
        <div class="chat-panel__header-left">
          <el-icon class="chat-panel__brand-icon"><ChatDotRound /></el-icon>
          <span class="chat-panel__brand-text">
            闲鱼对话
            <el-tooltip content="卖家对话页用于与闲鱼卖家进行实时沟通，查看商品详情，并可直接下单购买" placement="bottom" effect="dark">
              <el-icon style="margin-left: 4px; cursor: help; color: #909399; font-size: 14px;"><QuestionFilled /></el-icon>
            </el-tooltip>
          </span>
          <el-select
            v-model="chatStore.selectedClientId"
            placeholder="选择聊天客户端"
            size="small"
            class="chat-panel__client-select"
            clearable
            @change="handleClientChange"
          >
            <el-option
              v-for="client in availableClients"
              :key="client.client_id"
              :label="formatClientLabel(client)"
              :value="client.client_id"
            />
          </el-select>
        </div>
        <el-button link type="info" @click="chatStore.setVisible(false)" size="large" class="chat-panel__close-btn">
          <el-icon :size="20"><Close /></el-icon>
        </el-button>
      </header>

      <section class="chat-panel__body">
        <template v-if="!chatStore.selectedClientId">
          <div class="chat-panel__placeholder">
            <el-empty description="请选择一台在线的下单客户端用于聊天" />
          </div>
        </template>
        <template v-else>
          <aside class="chat-panel__sidebar">
            <div class="chat-panel__sidebar-header">
              <span>聊天列表</span>
              <div class="chat-panel__sidebar-actions">
                <el-icon v-if="sessionsLoading" class="is-loading" style="margin-right: 8px;"><Loading /></el-icon>
                <el-tooltip :content="showSessionSearch ? '关闭搜索' : '搜索会话'">
                  <el-button circle size="small" @click="toggleSessionSearch">
                    <el-icon><Search /></el-icon>
                  </el-button>
                </el-tooltip>
                <el-tooltip content="刷新会话列表">
                  <el-button circle size="small" @click="refreshSessions(true)" :disabled="sessionsLoading">
                    <el-icon><Refresh /></el-icon>
                  </el-button>
                </el-tooltip>
              </div>
            </div>
            <!-- ✅ 会话搜索框（点击按钮显示/隐藏） -->
            <transition name="el-zoom-in-top">
              <div v-if="showSessionSearch" class="chat-panel__search">
                <el-input
                  v-model="sessionSearchQuery"
                  placeholder="搜索卖家名称、商品标题..."
                  clearable
                  size="small"
                  @input="handleSessionSearch"
                  @clear="sessionSearchQuery = ''"
                >
                  <template #prefix>
                    <el-icon><Search /></el-icon>
                  </template>
                </el-input>
              </div>
            </transition>
            <div v-if="filteredSessions.length" class="chat-session-list">
              <el-scrollbar 
                ref="sessionScrollRef"
                style="height: 100%; flex: 1; overscroll-behavior: contain;" 
                @wheel="handleSessionWheel"
                @scroll="handleSessionScroll"
              >
                <div
                  v-for="session in filteredSessions"
                  :key="session.sessionId"
                  class="chat-session-item"
                  :class="{ active: session.sessionId === activeSessionId }"
                  @click="handleSelectSession(session.sessionId)"
                  @contextmenu.prevent="handleSessionContextMenu($event, session)"
                >
                  <div class="chat-session-item__avatar">
                    <el-avatar :size="38" :src="getSellerAvatar(session)" />
                    <!-- ✅ 商品已下架标记 -->
                    <span v-if="session.itemStatus === 'deleted'" class="chat-session-item__error-badge" title="商品已下架">
                      <el-icon><Close /></el-icon>
                    </span>
                    <!-- ✅ 未读消息标记 -->
                    <span v-else-if="session.unread > 0" class="chat-session-item__badge">
                      {{ session.unread > 99 ? '99+' : session.unread }}
                    </span>
                  </div>
                  <div class="chat-session-item__content">
                    <div class="chat-session-item__title" :title="session.sellerName || '未知卖家'">
                      {{ session.sellerName || '未知卖家' }}
                    </div>
                    <div class="chat-session-item__message" :title="session.lastMessage || ''">
                      {{ session.lastMessage || '暂无消息' }}
                    </div>
                  </div>
                  <div class="chat-session-item__time">
                    {{ formatRelativeTime(session.lastTime) }}
                  </div>
                </div>
                <!-- ✅ 底部加载更多提示 -->
                <div v-if="loadingMoreSessions" style="text-align: center; padding: 10px; color: #909399; font-size: 12px;">
                  <el-icon class="is-loading" style="vertical-align: middle; margin-right: 4px;"><Loading /></el-icon>
                  <span>加载更多会话...</span>
                </div>
                <div v-if="!hasMoreSessions && filteredSessions.length > 10" style="text-align: center; padding: 10px; color: #909399; font-size: 12px;">
                  <span>没有更多会话了</span>
                </div>
              </el-scrollbar>
            </div>
            <div v-else class="chat-session-empty">
              <el-empty 
                :description="sessionSearchQuery ? '未找到匹配的会话' : '暂无会话'" 
                :image-size="70" 
              />
            </div>
          </aside>

          <main class="chat-panel__main">
            <template v-if="currentSession">
              <div class="chat-main__header">
                <div class="chat-main__seller">
                  <div class="chat-main__seller-name">{{ currentSession.sellerName || '未知卖家' }}</div>
                  <div class="chat-main__seller-sub">卖家 ID：{{ currentSession.sellerId }}</div>
                </div>
              </div>
              <el-scrollbar class="chat-main__messages" ref="messageScrollRef" @wheel="handleWheel" @scroll="handleScroll" style="overscroll-behavior: contain;">
                <!-- ✅ 加载更多提示 -->
                <div v-if="loadingMoreMessages" class="chat-main__loading-more">
                  <el-icon class="is-loading"><Loading /></el-icon>
                  <span>加载更多消息...</span>
                </div>
                <div v-if="!messages.length" class="chat-main__no-messages">
                  <el-empty description="暂无消息，开始对话吧" :image-size="100" />
                </div>
                <div
                  v-for="msg in messages"
                  :key="msg.messageId || msg.localId"
                  class="chat-bubble"
                  :class="{ 
                    self: msg.direction === 'send',
                    pending: msg.pending,
                    failed: msg.status === 'failed'
                  }"
                >
                  <div class="chat-bubble__content">
                    <!-- ✅ 图片消息 -->
                    <div v-if="msg.message_type === 'image'" class="chat-message-image">
                      <el-image
                        :src="msg.image_url"
                        fit="cover"
                        :lazy="true"
                        :preview-src-list="[msg.image_url]"
                        :preview-teleported="true"
                        :hide-on-click-modal="true"
                        :z-index="9999"
                        @click="handleImageClick(msg.image_url)"
                        style="max-width: 300px; max-height: 300px; border-radius: 8px; cursor: pointer;"
                        :style="{
                          width: msg.image_width && msg.image_height ? 
                            `${Math.min(300, (msg.image_width / msg.image_height) * 200)}px` : 'auto',
                          height: msg.image_width && msg.image_height ? 
                            `${Math.min(300, (msg.image_height / msg.image_width) * 200)}px` : 'auto'
                        }"
                      >
                        <template #placeholder>
                          <div class="image-slot" style="display: flex; align-items: center; justify-content: center; height: 100px; background: #f5f7fa; border-radius: 8px;">
                            <el-icon class="is-loading"><Loading /></el-icon>
                            <span style="margin-left: 8px;">加载中...</span>
                          </div>
                        </template>
                        <template #error>
                          <div class="image-slot" style="display: flex; align-items: center; justify-content: center; height: 100px; background: #f5f7fa; border-radius: 8px;">
                            <el-icon><Picture /></el-icon>
                            <span style="margin-left: 8px;">图片加载失败</span>
                          </div>
                        </template>
                      </el-image>
                      <!-- 调试：显示图片URL -->
                      <div style="font-size: 10px; color: #999; margin-top: 4px; word-break: break-all;" v-if="false">
                        {{ msg.image_url }}
                      </div>
                    </div>
                    
                    <!-- ✅ 系统消息（textCard） -->
                    <div v-else-if="(msg.message_type === 'system' || msg.custom_content_type === 6) && (msg.system_data || msg.message_content)" class="chat-message-system">
                      <el-alert
                        :title="msg.system_data?.title || '系统消息'"
                        type="warning"
                        :closable="false"
                        show-icon
                        style="max-width: 100%;"
                      >
                        <template #default>
                          <div v-html="formatSystemContent(msg)" class="system-message-content"></div>
                        </template>
                      </el-alert>
                    </div>
                    
                    <!-- ✅ 语音消息 -->
                    <div v-else-if="msg.message_type === 'voice' || (msg.message_type === 'audio')" class="chat-message-voice">
                      <audio
                        :ref="el => { if (el) voiceAudioRefs.push(el) }"
                        :data-message-id="msg.messageId || msg.localId"
                        :src="msg.voice_url"
                        preload="none"
                        crossOrigin="anonymous"
                        @loadedmetadata="handleVoiceLoaded"
                        @timeupdate="handleVoiceTimeUpdate"
                        @ended="handleVoiceEnded"
                        @error="handleVoiceError"
                      >
                        <!-- ✅ 不再使用多个source元素，直接使用audio的src属性，避免多个206请求 -->
                      </audio>
                      <div class="voice-player" @click="handleVoicePlay(msg)">
                        <el-icon class="voice-icon" :class="{ playing: isVoicePlaying(msg) }">
                          <VideoPlay v-if="!isVoicePlaying(msg)" />
                          <VideoPause v-else />
                        </el-icon>
                        <div class="voice-wave">
                          <div class="voice-wave-bar" v-for="i in 20" :key="i" :class="{ active: isVoicePlaying(msg) }"></div>
                        </div>
                        <span class="voice-duration">
                          {{ formatVoiceDuration(msg) }}
                        </span>
                      </div>
                    </div>
                    
                    <!-- ✅ 视频消息 -->
                    <div v-else-if="(msg.message_type === 'video') && (msg.video_url || (msg.extra_data && msg.extra_data.video_url))" class="chat-message-video">
                      <video 
                        :src="msg.video_url || (msg.extra_data && msg.extra_data.video_url)" 
                        :poster="msg.video_cover"
                        controls 
                        controlsList="nodownload"
                        style="max-width: 300px; max-height: 400px; border-radius: 8px;"
                        @error="handleVideoError"
                      >
                        您的浏览器不支持视频播放
                      </video>
                      <div v-if="msg.video_duration" class="video-duration">
                        {{ formatVideoDuration(msg.video_duration) }}
                      </div>
                    </div>
                    
                    <!-- ✅ 文本消息或默认显示 -->
                    <!-- ⚠️ 注意：如果message_type是voice或image但没有URL，也显示文本内容 -->
                    <template v-else>
                      {{ msg.message_content || msg.content || '[消息]' }}
                    </template>
                    
                    <!-- ✅ 发送状态指示 -->
                    <span v-if="msg.pending" class="chat-bubble__status">
                      <el-icon class="is-loading"><Loading /></el-icon>
                    </span>
                    <span v-else-if="msg.status === 'failed'" class="chat-bubble__status error">
                      <el-icon><WarningFilled /></el-icon>
                      <el-tooltip :content="msg.error || '发送失败'" placement="top">
                        <span class="error-text">发送失败</span>
                      </el-tooltip>
                    </span>
                    <!-- ✅ 已读状态显示（仅对发送的消息） -->
                    <span v-else-if="msg.direction === 'send' && (msg.read_status === 2 || msg.readStatus === 2)" class="chat-bubble__read-status read">
                      <el-tooltip content="对方已读" placement="top">
                        <el-icon><Select /></el-icon>
                      </el-tooltip>
                    </span>
                  </div>
                  <div class="chat-bubble__time">
                    {{ formatMessageTime(msg.message_time) }}
                    <!-- ✅ 已读状态文本（仅对发送的消息） -->
                    <span v-if="msg.direction === 'send' && (msg.read_status === 2 || msg.readStatus === 2)" class="read-status-text">已读</span>
                  </div>
                </div>
              </el-scrollbar>
              <div class="chat-main__input-wrapper">
                <div class="chat-main__input">
                  <div class="chat-input-toolbar">
                    <el-button
                      circle
                      size="small"
                      @click="showEmojiPicker = !showEmojiPicker"
                      title="表情"
                    >
                      <el-icon><ChatDotRound /></el-icon>
                    </el-button>
                  </div>
                  <el-input
                    v-model="draftMessage"
                    type="textarea"
                    :rows="3"
                    maxlength="500"
                    show-word-limit
                    placeholder="输入要发送给卖家的消息...（支持粘贴图片）"
                    @keydown.enter.ctrl="handleSend"
                    @paste="handlePaste"
                    ref="messageInputRef"
                  />
                  <!-- 表情包选择器 -->
                  <div v-if="showEmojiPicker" class="emoji-picker">
                    <div class="emoji-picker__header">
                      <span>选择表情</span>
                      <el-button link size="small" @click="showEmojiPicker = false">
                        <el-icon><Close /></el-icon>
                      </el-button>
                    </div>
                    <div class="emoji-picker__content" v-loading="emojiLoading">
                      <div v-if="taobaoEmojis.length === 0 && !emojiLoading" class="emoji-empty">
                        <el-empty description="暂无表情包" :image-size="60" />
                      </div>
                      <div v-else class="emoji-grid">
                        <div
                          v-for="emoji in taobaoEmojis"
                          :key="emoji.code"
                          class="emoji-item"
                          @click="insertEmoji(emoji)"
                          :title="emoji.name"
                        >
                          <img :src="emoji.url" :alt="emoji.name" />
                        </div>
                      </div>
                    </div>
                  </div>
                  <div class="chat-main__input-actions">
                    <el-button type="primary" :disabled="!draftMessage.trim()" @click="handleSend">
                      发送 (Ctrl+Enter)
                    </el-button>
                  </div>
                </div>
              </div>
            </template>
            <template v-else>
              <div class="chat-main__empty">
                <el-empty description="选择左侧的卖家以查看会话" />
              </div>
            </template>
          </main>

          <aside class="chat-panel__info" v-if="currentSession">
            <div class="chat-info__section">
              <div class="chat-info__title">商品信息</div>
              <div class="chat-info__content">
                <!-- 商品图片/视频 -->
                <div class="chat-info__image" v-if="currentSession.itemImage || currentSession.item_image || (currentSession.itemDetail && currentSession.itemDetail.videoPlayInfo)">
                  <!-- 图片 -->
                  <el-image
                    v-if="!currentPreviewMedia || currentPreviewMedia.type === 'image'"
                    :src="displayMainImage"
                    fit="cover"
                    style="width: 100%; height: 200px;"
                    @click="openFullScreenViewer"
                  >
                    <template #error>
                      <div style="display: flex; align-items: center; justify-content: center; height: 100%; background: #f5f7fa;">
                        <el-icon><Picture /></el-icon>
                      </div>
                    </template>
                  </el-image>
                  <!-- 视频 -->
                  <video
                    v-else-if="currentPreviewMedia && currentPreviewMedia.type === 'video'"
                    :src="currentPreviewMedia.playUrl || currentPreviewMedia.url"
                    controls
                    muted
                    loop
                    style="width: 100%; height: 200px; object-fit: cover; background: #000; cursor: pointer;"
                    @dblclick="openFullScreenViewer"
                  />  
                  <!-- ✅ 商品状态水印遮罩 (已卖掉/已下架) -->
                  <div 
                    v-if="currentSession.itemStatus === 1 || currentSession.itemStatus === '1' || currentSession.itemStatus === 'deleted' || currentSession.itemStatus === -2 || currentSession.itemStatus === '-2'" 
                    class="sold-out-overlay"
                  >
                    <div class="sold-out-badge">
                      <div class="sold-out-content">
                        <span class="sold-out-text-main">{{ (currentSession.itemStatus === 'deleted' || currentSession.itemStatus === -2 || currentSession.itemStatus === '-2') ? '已下架' : '卖掉了' }}</span>
                        <span class="sold-out-text-sub">{{ (currentSession.itemStatus === 'deleted' || currentSession.itemStatus === -2 || currentSession.itemStatus === '-2') ? 'DELETED' : 'SOLD OUT' }}</span>
                      </div>
                      <div class="sold-out-lines">
                        <span class="line"></span>
                        <span class="line"></span>
                        <span class="line"></span>
                        <span class="line"></span>
                      </div>
                    </div>
                  </div>
                  <!-- ✅ 缩略图列表（当有多个媒体时显示） -->
                  <div v-if="currentItemMediaList.length > 0" class="chat-info__thumbnails">
                    <div 
                      v-for="(media, index) in currentItemMediaList" 
                      :key="index"
                      class="thumbnail-item"
                      :class="{ active: currentPreviewIndex === index }"
                      @click="switchPreviewImage(index)"
                    >
                      <el-image
                        :src="media.url"
                        fit="cover"
                        style="width: 100%; height: 100%;"
                      >
                        <template #error>
                          <div style="display: flex; align-items: center; justify-content: center; height: 100%; background: #f5f7fa;">
                            <el-icon><Picture /></el-icon>
                          </div>
                        </template>
                      </el-image>
                      <!-- 视频标识 -->
                      <div v-if="media.type === 'video'" class="thumbnail-video-badge">
                        <el-icon :size="12"><VideoPlay /></el-icon>
                      </div>
                    </div>
                  </div>
                </div>
                
                <!-- 商品标题 -->
                <div class="chat-info__title-section" v-if="currentSession.itemTitle">
                  <el-tooltip
                    :content="currentSession.itemTitle"
                    placement="top"
                    effect="dark"
                    :disabled="!currentSession.itemTitle || currentSession.itemTitle.length <= 30"
                  >
                    <div 
                      class="chat-info__item-title" 
                      :class="{ 'clickable': currentSession.itemId }"
                      @click="handleOpenItemDetail"
                      :title="currentSession.itemId ? '点击打开商品详情页' : ''"
                    >
                      {{ currentSession.itemTitle }}
                    </div>
                  </el-tooltip>
                </div>
                
                <!-- 价格 -->
                <div class="chat-info__price-section" v-if="currentSession.itemPrice">
                  <span class="chat-info__price">￥{{ currentSession.itemPrice }}</span>
                </div>
                
                <!-- 标签 -->
                <div class="chat-info__tags" v-if="currentSession.itemTags && currentSession.itemTags.length > 0">
                  <el-tag
                    v-for="(tag, index) in uniqueItemTags"
                    :key="`${tag}-${index}`"
                    size="small"
                    :type="getTagType(tag)"
                    style="margin-right: 4px; margin-bottom: 4px;"
                  >
                    {{ tag }}
                  </el-tag>
                </div>
                
                <!-- 商品ID和卖家ID（折叠显示） -->
                <el-collapse v-model="showItemDetails" style="margin-top: 8px;">
                  <el-collapse-item name="details" :title="'详细信息'" style="border: none;">
                    <div class="chat-info__item">
                      <span class="label">商品ID</span>
                      <span class="value">{{ currentSession.itemId || '-' }}</span>
                    </div>
                    <div class="chat-info__item">
                      <span class="label">卖家ID</span>
                      <span class="value">{{ currentSession.sellerId || '-' }}</span>
                    </div>
                  </el-collapse-item>
                </el-collapse>
              </div>
              
              <!-- 下单按钮（底部对齐） -->
              <!-- ⚠️ 异常状态商品不显示订单操作按钮 -->
              <div 
                v-if="!(currentSession.itemStatus === 1 || currentSession.itemStatus === '1' || currentSession.itemStatus === -2 || currentSession.itemStatus === '-2' || currentSession.itemStatus === 'deleted')"
                class="chat-info__actions"
              >
                <el-button 
                  :type="orderButtonType"
                  size="default" 
                  @click="handleOrderAction"
                  :disabled="!currentSession.itemId || orderButtonLoading"
                  :loading="orderButtonLoading"
                  style="width: 100%;"
                >
                  {{ orderButtonText }}
                </el-button>
              </div>
            </div>
          </aside>
        </template>
      </section>
      
      <!-- ✅ 图片查看器（使用Element Plus的el-image-viewer，支持放大缩小、旋转等） -->
      <!-- teleport到body，确保z-index最高，不受父容器影响 -->
      <Teleport to="body">
        <el-image-viewer
          v-if="showImageViewer"
          :url-list="imageUrlList"
          :initial-index="currentImageIndex"
          @close="showImageViewer = false"
          :zoom-rate="1.2"
          :hide-on-click-modal="true"
        />
      </Teleport>
        
        <!-- ✅ 视频播放器（全屏） -->
        <el-dialog
          v-model="showVideoPlayer"
          fullscreen
          append-to-body
          destroy-on-close
          class="video-player-dialog"
        >
          <div class="video-player-fullscreen">
            <!-- 关闭按钮 -->
            <div class="video-player-close" @click="showVideoPlayer = false">
              <el-icon :size="24"><Close /></el-icon>
            </div>
            
            <!-- 视频播放 -->
            <div class="video-player-content">
              <video
                v-if="currentVideoUrl"
                :src="currentVideoUrl"
                controls
                autoplay
                style="max-width: 100%; max-height: 100%;"
              >
                您的浏览器不支持视频播放
              </video>
            </div>
          </div>
        </el-dialog>
      
      <!-- 调整大小手柄 -->
      <!-- ✅ 右下角：同时调整宽度和高度 -->
      <div class="chat-panel__resize-handle" @mousedown="handleResizeMouseDown"></div>
      <!-- ✅ 右侧：仅调整宽度 -->
      <div class="chat-panel__resize-handle-vertical" @mousedown="handleResizeMouseDownVertical"></div>
      <!-- ✅ 底部：仅调整高度 -->
      <div class="chat-panel__resize-handle-horizontal" @mousedown="handleResizeMouseDownHorizontal"></div>
      
      <!-- ✅ 右键菜单 -->
      <teleport to="body">
        <div
          v-if="contextMenu.visible"
          class="chat-context-menu"
          :style="{ left: contextMenu.x + 'px', top: contextMenu.y + 'px' }"
          @click.stop
        >
          <div class="context-menu-item" @click.stop="handlePinSession">
            <el-icon><Top /></el-icon>
            <span>置顶</span>
          </div>
          <div class="context-menu-item" @click.stop="handleHideSessionFromMenu">
            <el-icon><Delete /></el-icon>
            <span>删除会话</span>
          </div>
          <div class="context-menu-divider"></div>
          <!-- 根据黑名单状态显示不同选项 -->
          <div 
            v-if="contextMenu.session?.isBlocked" 
            class="context-menu-item context-menu-item--success" 
            @click.stop="handleUnblockUserFromMenu"
          >
            <el-icon><CircleCheck /></el-icon>
            <span>移除黑名单</span>
          </div>
          <div 
            v-else
            class="context-menu-item context-menu-item--danger" 
            @click.stop="handleBlockUserFromMenu"
          >
            <el-icon><CircleClose /></el-icon>
            <span>加入黑名单</span>
          </div>
        </div>
      </teleport>
    </div>
  </transition>
</template>

<script setup>
import { computed, ref, watch, nextTick, onMounted, onActivated, onUnmounted } from 'vue'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import 'dayjs/locale/zh-cn'
import { ElMessage, ElLoading, ElMessageBox } from 'element-plus'
import { ChatDotRound, Close, Refresh, Picture, QuestionFilled, Loading, WarningFilled, VideoPlay, VideoPause, Top, Delete, CircleClose, CircleCheck, Select, Search, ArrowLeft, ArrowRight, ZoomIn } from '@element-plus/icons-vue'
import useChatStore from '@/store/modules/chat'
import { useAgentStore } from '@/store/modules/agent'
import { getChatSessions, getChatMessages, sendChatMessage, markChatRead, openChat } from '@/api/chat'
import { websocketManager } from '@/utils/websocket'
import { getOrderList, manualOrder, cancelOrder, getAgentClientList } from '@/api/agent'
import request from '@/utils/request'
import { generateTempId, generateMid, MessageStatus } from '@/utils/messageStatus'
import { getCachedSessions, setCachedSessions, clearCachedSessions } from '@/utils/chatCache'

dayjs.extend(relativeTime)
dayjs.locale('zh-cn')

const chatStore = useChatStore()
const agentStore = useAgentStore()
const draftMessage = ref('')
const messageScrollRef = ref(null)
const defaultAvatar = 'https://cdn.jsdelivr.net/gh/placeholderjs/placeholder-avatar@latest/avatar.png'
const showItemDetails = ref([])
const panelRef = ref(null)
const voiceAudioRefs = ref([])
const playingVoiceMessageId = ref(null)  // 当前播放的语音消息ID
const voiceCurrentTime = ref({})  // 存储每个语音消息的当前播放时间

// ✅ 历史消息加载状态
const loadingMoreMessages = ref(false)
const messagePageNum = ref({})  // 每个会话的当前页码，格式：{ sessionId: pageNum }
const hasMoreMessages = ref({})  // 每个会话是否还有更多消息，格式：{ sessionId: boolean }

// ✅ openChat请求去重：避免短时间内重复调用
const pendingOpenChatRequests = new Map()  // key: `${clientId}_${sellerId}_${itemId}`, value: Promise

/**
 * 包装openChat调用，添加请求去重机制
 * @param {Object} params - {client_id, seller_id, item_id, seller_name}
 * @returns {Promise}
 */
async function openChatWithDedup(params) {
  const key = `${params.client_id}_${params.seller_id}_${params.item_id}`
  
  // ✅ 如果已有相同的请求正在进行，直接返回该Promise
  if (pendingOpenChatRequests.has(key)) {
    console.log('[ChatPanel] ⚠️ openChat请求去重，复用现有请求:', {
      key,
      params_sent: params,
      note: '注意：seller_name可能不同，但后端返回数据完全相同（只依赖item_id）'
    })
    return pendingOpenChatRequests.get(key)
  }
  
  // ✅ 创建新请求
  console.log('[ChatPanel] 🚀 openChat新请求:', { key, params })
  const requestPromise = openChat(params).finally(() => {
    // ✅ 请求完成后移除（无论成功或失败）
    pendingOpenChatRequests.delete(key)
    console.log('[ChatPanel] ✅ openChat请求完成，已清理缓存:', key)
  })
  
  // ✅ 存储到Map中
  pendingOpenChatRequests.set(key, requestPromise)
  
  return requestPromise
}
const amrPlayerInstances = ref(new Map())  // ✅ 存储AMR播放器实例，用于暂停/停止

// 表情包相关
const showEmojiPicker = ref(false)
const taobaoEmojis = ref([])  // 淘宝表情包列表
const emojiLoading = ref(false)
const messageInputRef = ref(null)

// 会话加载状态
const sessionsLoading = ref(false)

// ✅ 会话搜索
const sessionSearchQuery = ref('')
const showSessionSearch = ref(false)

// ✅ 切换搜索框显示/隐藏
const toggleSessionSearch = () => {
  showSessionSearch.value = !showSessionSearch.value
  // 关闭搜索框时清空搜索关键词
  if (!showSessionSearch.value) {
    sessionSearchQuery.value = ''
  }
}

// ✅ 媒体查看器（图片/视频浏览）
const showMediaViewer = ref(false)
const showImageViewer = ref(false)
const showVideoPlayer = ref(false)
const currentVideoUrl = ref('')
const mediaViewerIndex = ref(0)
const currentPreviewIndex = ref(0)  // 当前预览的缩略图索引

// ✅ 当前会话的媒体列表（图片+视频）
const currentItemMediaList = computed(() => {
  if (!currentSession.value) return []
  
  const mediaList = []
  const itemDetail = currentSession.value.itemDetail
  
  console.log('[ChatPanel] 当前会话:', currentSession.value.sessionId)
  console.log('[ChatPanel] itemDetail:', itemDetail)
  
  if (!itemDetail) {
    // 如果没有详情，只显示主图
    if (currentSession.value.itemImage) {
      mediaList.push({
        type: 'image',
        url: currentSession.value.itemImage
      })
    }
    console.log('[ChatPanel] 无itemDetail，只显示主图，mediaList长度:', mediaList.length)
    return mediaList
  }
  
  // 优先添加视频（如果存在）
  if (itemDetail.videoPlayInfo) {
    mediaList.push({
      type: 'video',
      url: itemDetail.videoPlayInfo.url,
      playUrl: itemDetail.videoPlayInfo.playUrl,
      width: itemDetail.videoPlayInfo.widthSize,
      height: itemDetail.videoPlayInfo.heightSize
    })
  }
  
  // 添加所有图片
  if (itemDetail.imageInfos && Array.isArray(itemDetail.imageInfos)) {
    console.log('[ChatPanel] imageInfos数量:', itemDetail.imageInfos.length)
    itemDetail.imageInfos.forEach(img => {
      mediaList.push({
        type: 'image',
        url: img.url,
        width: img.widthSize,
        height: img.heightSize
      })
    })
  } else {
    console.log('[ChatPanel] imageInfos不存在或不是数组')
  }
  
  console.log('[ChatPanel] 最终mediaList长度:', mediaList.length)
  return mediaList
})

// ✅ 当前查看的媒体
const currentMedia = computed(() => {
  if (mediaViewerIndex.value >= 0 && mediaViewerIndex.value < currentItemMediaList.value.length) {
    return currentItemMediaList.value[mediaViewerIndex.value]
  }
  return null
})

// ✅ 按需加载商品详情（图片列表、视频）
const loadItemDetailIfNeeded = async () => {
  const session = currentSession.value
  if (!session || !session.itemId) return false
  
  // 如果已有 itemDetail，直接返回
  if (session.itemDetail && session.itemDetail.imageInfos && session.itemDetail.imageInfos.length > 0) {
    return true
  }
  
  // 如果是已下架商品，不加载
  const isDelistedItem = session.itemStatus === 'deleted' || session.itemStatus === -2 || session.itemStatus === '-2'
  if (isDelistedItem) return false
  
  console.log('[ChatPanel] 按需加载商品详情:', session.itemId)
  
  try {
    const response = await openChatWithDedup({
      client_id: chatStore.selectedClientId,
      seller_id: session.sellerId,
      item_id: session.itemId,
      seller_name: session.sellerName
    })
    
    if (response.code === 200 && response.data) {
      // 更新会话信息
      chatStore.addOrUpdateSession({
        sessionId: session.sessionId,
        itemDetail: {
          imageInfos: response.data.image_infos || [],
          videoPlayInfo: response.data.video_play_info || null
        }
      })
      console.log('[ChatPanel] ✅ 商品详情加载成功')
      return true
    }
  } catch (error) {
    console.error('[ChatPanel] 加载商品详情失败:', error)
  }
  
  return false
}

// ✅ 当前预览的媒体项（用于判断类型）
const currentPreviewMedia = computed(() => {
  if (currentItemMediaList.value.length > 0 && currentPreviewIndex.value >= 0 && currentPreviewIndex.value < currentItemMediaList.value.length) {
    return currentItemMediaList.value[currentPreviewIndex.value]
  }
  return null
})

// ✅ 主图显示逻辑：默认显示 itemImage，点击缩略图后才切换
const displayMainImage = computed(() => {
  // 如果有预览媒体且是图片，直接显示预览媒体
  if (currentPreviewMedia.value && currentPreviewMedia.value.type === 'image') {
    return currentPreviewMedia.value.url
  }
  
  // 否则显示原始主图
  return currentSession.value?.itemImage || currentSession.value?.item_image || ''
})

// ✅ 所有图片URL列表（用于el-image-viewer）
const imageUrlList = computed(() => {
  return currentItemMediaList.value
    .filter(m => m.type === 'image')
    .map(m => m.url)
})

// ✅ 当前图片在imageUrlList中的索引（用于el-image-viewer）
const currentImageIndex = computed(() => {
  if (!currentPreviewMedia.value || currentPreviewMedia.value.type !== 'image') return 0
  const imgUrl = currentPreviewMedia.value.url
  return imageUrlList.value.indexOf(imgUrl)
})

// ✅ 切换预览图片（点击缩略图）
const switchPreviewImage = async (index) => {
  // 先加载详情
  await loadItemDetailIfNeeded()
  
  currentPreviewIndex.value = index
  console.log('[ChatPanel] 切换预览图片:', index)
}

// ✅ 打开全屏查看器（点击主图）
const openFullScreenViewer = async () => {
  // 先加载详情
  await loadItemDetailIfNeeded()
  
  const media = currentItemMediaList.value[currentPreviewIndex.value]
  
  if (media?.type === 'video') {
    // 视频：打开视频播放器
    currentVideoUrl.value = media.playUrl || media.url
    showVideoPlayer.value = true
  } else {
    // 图片：打开图片查看器
    showImageViewer.value = true
  }
  
  console.log('[ChatPanel] 打开全屏查看器:', media?.type, currentPreviewIndex.value)
}

// ✅ 视频播放错误处理
const handleVideoError = (e) => {
  console.error('[ChatPanel] 视频播放失败:', e)
  ElMessage.error('视频播放失败，请检查网络或视频链接')
}

// ✅ 图片加载错误处理
const handleImageError = (e) => {
  console.error('[ChatPanel] 图片加载失败:', e)
  ElMessage.error('图片加载失败')
}

// ✅ 图片加载成功处理
const handleImageLoad = (e) => {
  console.log('[ChatPanel] 图片加载成功')
}

// ✅ 防重复提交：跟踪正在发送已读回执的会话
const markReadLocks = new Map()  // key: sessionId, value: Promise

// 面板位置和大小（可拖动和调整大小）
const panelPosition = ref({ x: null, y: null }) // null表示使用默认位置
const panelSize = ref({ width: 1000, height: 600 }) // 默认大小
const isDragging = ref(false)
const isResizing = ref(false)
const dragStart = ref({ x: 0, y: 0 })

// ✅ 右键菜单状态
const contextMenu = ref({
  visible: false,
  x: 0,
  y: 0,
  session: null
})

// ✅ 关闭菜单的清理函数（保存在外部，避免重复创建）
let contextMenuCleanup = null

// ✅ 处理面板点击（关闭右键菜单）
const handlePanelClick = () => {
  if (contextMenu.value.visible) {
    console.log('[ContextMenu] 面板点击，关闭菜单')
    contextMenu.value.visible = false
    if (contextMenuCleanup) {
      contextMenuCleanup()
      contextMenuCleanup = null
    }
  }
}

// ✅ 全局ESC键监听：优先关闭菜单，否则关闭聊天窗
const handleGlobalEscape = (e) => {
  if (e.key === 'Escape' || e.key === 'Esc') {
    // ⚠️ 检查是否有 Element Plus 图片预览器正在显示
    const imageViewer = document.querySelector('.el-image-viewer__wrapper')
    if (imageViewer) {
      console.log('[ChatPanel] 检测到图片预览器，由其自身处理ESC键')
      return // 让图片预览器自己处理ESC键
    }
    
    // ⚠️ 检查是否有 MessageBox 或其他弹窗
    const messageBox = document.querySelector('.el-message-box__wrapper')
    if (messageBox) {
      console.log('[ChatPanel] 检测到弹窗，由其自身处理ESC键')
      return // 让弹窗自己处理ESC键
    }
    
    // 如果菜单打开，先关闭菜单
    if (contextMenu.value.visible) {
      console.log('[ChatPanel] ESC键关闭右键菜单')
      contextMenu.value.visible = false
      if (contextMenuCleanup) {
        contextMenuCleanup()
        contextMenuCleanup = null
      }
    } else {
      // 否则关闭聊天窗体
      console.log('[ChatPanel] ESC键关闭聊天窗')
      chatStore.setVisible(false)
    }
  }
}

// ✅ 组件挂载时添加ESC键监听
onMounted(() => {
  document.addEventListener('keydown', handleGlobalEscape)
  console.log('[ChatPanel] 已添加ESC键监听')
})

// ✅ 组件卸载时移除ESC键监听
onUnmounted(() => {
  document.removeEventListener('keydown', handleGlobalEscape)
  console.log('[ChatPanel] 已移除ESC键监听')
})

// ✅ 处理右键菜单
const handleSessionContextMenu = (event, session) => {
  console.log('[ContextMenu] 右键菜单触发', { session: session.sessionId })
  
  // 先清理之前的监听器
  if (contextMenuCleanup) {
    console.log('[ContextMenu] 清理旧的监听器')
    contextMenuCleanup()
    contextMenuCleanup = null
  }
  
  contextMenu.value = {
    visible: true,
    x: event.clientX,
    y: event.clientY,
    session
  }
  
  console.log('[ContextMenu] 菜单已设置为可见', contextMenu.value)
  
  // ✅ 关闭菜单的函数
  const closeMenu = (e) => {
    console.log('[ContextMenu] 关闭菜单', e?.type)
    contextMenu.value.visible = false
    if (contextMenuCleanup) {
      contextMenuCleanup()
      contextMenuCleanup = null
    }
  }
  
  // ✅ 使用 nextTick 确保菜单渲染后再添加监听器
  // 避免当前的右键点击事件立即触发 closeMenu
  nextTick(() => {
    console.log('[ContextMenu] 添加文档监听器')
    document.addEventListener('click', closeMenu)
    // ⚠️ 不要监听 contextmenu，因为右键点击会冒泡导致菜单立即关闭
    // ⚠️ ESC键由全局监听器处理，这里不再重复添加
    
    // 保存清理函数
    contextMenuCleanup = () => {
      console.log('[ContextMenu] 执行清理函数')
      document.removeEventListener('click', closeMenu)
    }
  })
}

// ✅ 删除会话（实际是隐藏）
const handleDeleteSession = async (session) => {
  try {
    await ElMessageBox.confirm(
      `确定要删除与 "${session.sellerName || '未知卖家'}" 的会话吗？`,
      '删除会话',
      {
        confirmButtonText: '删除',
        cancelButtonText: '取消',
        type: 'warning',
        confirmButtonClass: 'el-button--danger'
      }
    )
    
    // 1. 立即从前端会话列表中移除
    const sessions = chatStore.sessions.filter(s => s.sessionId !== session.sessionId)
    chatStore.setSessions(sessions)
    
    // 2. 更新缓存
    setCachedSessions(chatStore.selectedClientId, sessions)
    
    // 3. 如果删除的是当前会话，清空当前会话
    if (chatStore.currentSessionId === session.sessionId) {
      chatStore.setCurrentSession(null)
    }
    
    // 4. 异步调用后端接口，通过WebSocket隐藏官方会话
    // 注意：这是隐藏而不是删除，如果对方继续发消息，会话会重新出现
    try {
      await request({
        url: '/agent/chat/hide',
        method: 'post',
        data: {
          client_id: chatStore.selectedClientId,
          conversation_id: session.sessionId
        }
      })
      console.log('[ChatPanel] ✅ 已调用官方hide接口隐藏会话:', session.sessionId)
    } catch (hideError) {
      console.warn('[ChatPanel] ⚠️ 调用官方hide接口失败（不影响前端隐藏）:', hideError)
    }
    
    ElMessage.success('会话已删除')
  } catch (error) {
    // 用户取消删除
    if (error !== 'cancel') {
      console.error('[ChatPanel] 删除会话失败:', error)
    }
  }
}

// ✅ 从右键菜单隐藏
const handleHideSessionFromMenu = () => {
  if (contextMenu.value.session) {
    handleDeleteSession(contextMenu.value.session)
  }
  contextMenu.value.visible = false
}

// ✅ 从右键菜单加入黑名单
const handleBlockUserFromMenu = () => {
  if (contextMenu.value.session) {
    handleBlockUser(contextMenu.value.session)
  }
  contextMenu.value.visible = false
}

// ✅ 从右键菜单移除黑名单
const handleUnblockUserFromMenu = () => {
  if (contextMenu.value.session) {
    handleUnblockUser(contextMenu.value.session)
  }
  contextMenu.value.visible = false
}

// ✅ 加入黑名单
const handleBlockUser = async (session) => {
  try {
    await ElMessageBox.confirm(
      `确定要将 "${session.sellerName || '未知卖家'}" 加入黑名单吗？\n\n加入黑名单后：\n• 对方无法给你发送消息\n• 你也无法给对方发送消息\n• 会话将被永久隐藏`,
      '加入黑名单',
      {
        confirmButtonText: '加入黑名单',
        cancelButtonText: '取消',
        type: 'error',
        confirmButtonClass: 'el-button--danger',
        dangerouslyUseHTMLString: false
      }
    )
    
    // 1. 立即从前端会话列表中移除
    const sessions = chatStore.sessions.filter(s => s.sessionId !== session.sessionId)
    chatStore.setSessions(sessions)
    
    // 2. 更新缓存
    setCachedSessions(chatStore.selectedClientId, sessions)
    
    // 3. 如果是当前会话，清空当前会话
    if (chatStore.currentSessionId === session.sessionId) {
      chatStore.setCurrentSession(null)
    }
    
    // 4. 调用后端接口加入黑名单（使用HTTP API）
    try {
      await request({
        url: '/agent/chat/block',
        method: 'post',
        data: {
          client_id: chatStore.selectedClientId,
          conversation_id: session.sessionId  // 后端会从中提取sessionId
        }
      })
      console.log('[ChatPanel] ✅ 已加入黑名单:', session.sessionId)
      ElMessage.success('已加入黑名单')
    } catch (blockError) {
      console.error('[ChatPanel] ❌ 加入黑名单失败:', blockError)
      ElMessage.error('加入黑名单失败，但已隐藏会话')
    }
  } catch (error) {
    // 用户取消
    if (error !== 'cancel') {
      console.error('[ChatPanel] 加入黑名单失败:', error)
    }
  }
}

// ✅ 移除黑名单
const handleUnblockUser = async (session) => {
  try {
    await ElMessageBox.confirm(
      `确定要将 "${session.sellerName || '未知卖家'}" 移除黑名单吗？\n\n移除后该用户可以正常给你发送消息。`,
      '移除黑名单',
      {
        confirmButtonText: '移除',
        cancelButtonText: '取消',
        type: 'warning'
      }
    )
    
    // 调用后端接口移除黑名单
    try {
      await request({
        url: '/agent/chat/unblock',
        method: 'post',
        data: {
          client_id: chatStore.selectedClientId,
          conversation_id: session.sessionId
        }
      })
      
      // 更新会话的黑名单状态
      const sessions = chatStore.sessions.map(s => {
        if (s.sessionId === session.sessionId) {
          return { ...s, isBlocked: false }
        }
        return s
      })
      chatStore.setSessions(sessions)
      setCachedSessions(chatStore.selectedClientId, sessions)
      
      console.log('[ChatPanel] ✅ 已移除黑名单:', session.sessionId)
      ElMessage.success('已移除黑名单')
    } catch (unblockError) {
      console.error('[ChatPanel] ❌ 移除黑名单失败:', unblockError)
      ElMessage.error('移除黑名单失败')
    }
  } catch (error) {
    // 用户取消
    if (error !== 'cancel') {
      console.error('[ChatPanel] 移除黑名单失败:', error)
    }
  }
}

// ✅ 置顶会话
const handlePinSession = () => {
  if (contextMenu.value.session) {
    ElMessage.info('置顶功能开发中...')
  }
  contextMenu.value.visible = false
}

// 计算面板样式
const panelStyle = computed(() => {
  const style = {}
  if (panelPosition.value.x !== null) {
    style.left = `${panelPosition.value.x}px`
    style.right = 'auto'
  } else {
    // 默认居中：计算居中位置
    const centerX = (window.innerWidth - panelSize.value.width) / 2
    style.left = `${Math.max(0, centerX)}px`
    style.right = 'auto'
  }
  if (panelPosition.value.y !== null) {
    style.top = `${panelPosition.value.y}px`
    style.bottom = 'auto'
  } else {
    // 默认居中：计算居中位置
    const centerY = (window.innerHeight - panelSize.value.height) / 2
    style.top = `${Math.max(0, centerY)}px`
    style.bottom = 'auto'
  }
  style.width = `${panelSize.value.width}px`
  style.height = `${panelSize.value.height}px`
  return style
})

// 处理头部拖拽
const handleHeaderMouseDown = (e) => {
  if (e.target.closest('.chat-panel__client-select') || e.target.closest('button')) {
    return // 如果点击的是下拉框或按钮，不触发拖拽
  }
  isDragging.value = true
  const rect = panelRef.value.getBoundingClientRect()
  dragStart.value = {
    x: e.clientX - rect.left,
    y: e.clientY - rect.top
  }
  
  const handleMouseMove = (e) => {
    if (!isDragging.value) return
    const x = e.clientX - dragStart.value.x
    const y = e.clientY - dragStart.value.y
    // 限制在视窗内
    const maxX = window.innerWidth - panelSize.value.width
    const maxY = window.innerHeight - panelSize.value.height
    panelPosition.value = {
      x: Math.max(0, Math.min(x, maxX)),
      y: Math.max(0, Math.min(y, maxY))
    }
  }
  
  const handleMouseUp = () => {
    isDragging.value = false
    document.removeEventListener('mousemove', handleMouseMove)
    document.removeEventListener('mouseup', handleMouseUp)
  }
  
  document.addEventListener('mousemove', handleMouseMove)
  document.addEventListener('mouseup', handleMouseUp)
}

// ✅ 处理调整大小（右下角：同时调整宽度和高度）
const handleResizeMouseDown = (e) => {
  e.preventDefault()
  e.stopPropagation()
  isResizing.value = true
  const startX = e.clientX
  const startY = e.clientY
  const startWidth = panelSize.value.width
  const startHeight = panelSize.value.height
  
  const handleMouseMove = (e) => {
    if (!isResizing.value) return
    const deltaX = e.clientX - startX
    const deltaY = e.clientY - startY
    const newWidth = Math.max(800, Math.min(startWidth + deltaX, window.innerWidth - 48))
    // ✅ 允许更大的高度，最大高度为窗口高度的90%（留出一些边距）
    const maxHeight = Math.floor(window.innerHeight * 0.9)
    const newHeight = Math.max(400, Math.min(startHeight + deltaY, maxHeight))
    panelSize.value = { width: newWidth, height: newHeight }
  }
  
  const handleMouseUp = () => {
    isResizing.value = false
    document.removeEventListener('mousemove', handleMouseMove)
    document.removeEventListener('mouseup', handleMouseUp)
  }
  
  document.addEventListener('mousemove', handleMouseMove)
  document.addEventListener('mouseup', handleMouseUp)
}

// ✅ 处理右侧调整宽度（仅调整宽度）
const handleResizeMouseDownVertical = (e) => {
  e.preventDefault()
  e.stopPropagation()
  isResizing.value = true
  const startX = e.clientX
  const startWidth = panelSize.value.width
  
  const handleMouseMove = (e) => {
    if (!isResizing.value) return
    const deltaX = e.clientX - startX
    const newWidth = Math.max(800, Math.min(startWidth + deltaX, window.innerWidth - 48))
    panelSize.value = { ...panelSize.value, width: newWidth }
  }
  
  const handleMouseUp = () => {
    isResizing.value = false
    document.removeEventListener('mousemove', handleMouseMove)
    document.removeEventListener('mouseup', handleMouseUp)
  }
  
  document.addEventListener('mousemove', handleMouseMove)
  document.addEventListener('mouseup', handleMouseUp)
}

// ✅ 处理底部调整高度（仅调整高度）
const handleResizeMouseDownHorizontal = (e) => {
  e.preventDefault()
  e.stopPropagation()
  isResizing.value = true
  const startY = e.clientY
  const startHeight = panelSize.value.height
  
  const handleMouseMove = (e) => {
    if (!isResizing.value) return
    const deltaY = e.clientY - startY
    // ✅ 允许更大的高度，最大高度为窗口高度的90%（留出一些边距）
    const maxHeight = Math.floor(window.innerHeight * 0.9)
    const newHeight = Math.max(400, Math.min(startHeight + deltaY, maxHeight))
    panelSize.value = { ...panelSize.value, height: newHeight }
  }
  
  const handleMouseUp = () => {
    isResizing.value = false
    document.removeEventListener('mousemove', handleMouseMove)
    document.removeEventListener('mouseup', handleMouseUp)
  }
  
  document.addEventListener('mousemove', handleMouseMove)
  document.addEventListener('mouseup', handleMouseUp)
}

// 聊天可用客户端：只使用下单类型且已登录的客户端（order类型 + logged_in状态）
const availableClients = computed(() => {
  if (!agentStore?.clients) return []
  return agentStore.clients.filter(c => 
    c.client_type === 'order' && 
    c.online_status === 'online' && 
    c.login_status === 'logged_in'
  )
})
const sessions = computed(() => chatStore.sessions)

// ✅ 过滤后的会话列表（根据搜索关键词）
const filteredSessions = computed(() => {
  if (!sessionSearchQuery.value || !sessionSearchQuery.value.trim()) {
    return sessions.value
  }
  
  const query = sessionSearchQuery.value.trim().toLowerCase()
  
  return sessions.value.filter(session => {
    // 搜索卖家名称
    const sellerName = (session.sellerName || '').toLowerCase()
    if (sellerName.includes(query)) {
      console.log('[ChatPanel] 搜索匹配（卖家）:', session.sellerName)
      return true
    }
    
    // 搜索商品标题
    const itemTitle = (session.itemTitle || '').toLowerCase()
    if (itemTitle.includes(query)) {
      console.log('[ChatPanel] 搜索匹配（标题）:', session.itemTitle)
      return true
    }
    
    // 搜索最后一条消息（排除表情等特殊字符）
    let lastMessage = (session.lastMessage || '').toLowerCase()
    // 移除常见的表情符号，只保留文字
    lastMessage = lastMessage.replace(/[\u{1F600}-\u{1F6FF}\u{2600}-\u{26FF}\u{1F300}-\u{1F5FF}]/gu, '')
    if (lastMessage.includes(query)) {
      console.log('[ChatPanel] 搜索匹配（消息）:', session.lastMessage)
      return true
    }
    
    return false
  })
})

const activeSessionId = computed(() => chatStore.currentSessionId)
const currentSession = computed(() => chatStore.currentSession)
// ✅ 使用当前会话ID获取消息，确保消息正确显示
const messages = computed(() => {
  const sessionId = chatStore.currentSessionId
  if (!sessionId) return []
  return chatStore.messages[sessionId] || []
})

// ✅ 去重标签（前端双重保险，即使后端已去重）
const uniqueItemTags = computed(() => {
  if (!currentSession.value?.itemTags || !Array.isArray(currentSession.value.itemTags)) {
    return []
  }
  // 使用Set去重，保留顺序
  const seen = new Set()
  return currentSession.value.itemTags.filter(tag => {
    const trimmedTag = String(tag || '').trim()
    if (!trimmedTag || seen.has(trimmedTag)) {
      return false
    }
    seen.add(trimmedTag)
    return true
  })
})

// 获取卖家头像
const getSellerAvatar = (session) => {
  // 如果有头像URL，使用它；否则使用默认头像
  return session.avatar || defaultAvatar
}

// 截断文本
const truncateText = (text, maxLength = 50) => {
  if (!text) return ''
  if (text.length <= maxLength) return text
  return text.substring(0, maxLength) + '...'
}

// 获取标签类型（用于样式）
const getTagType = (tag) => {
  if (tag.includes('包邮') || tag.includes('包邮')) return 'success'
  if (tag.includes('极好') || tag.includes('信用')) return 'warning'
  if (tag.includes('新') || tag.includes('新品')) return 'danger'
  return 'info'
}

// 处理客户端切换
const handleClientChange = async (clientId) => {
  console.log('[ChatPanel] 客户端切换:', clientId, '可用客户端:', availableClients.value.map(c => ({ 
    id: c.client_id, 
    name: c.client_name, 
    type: c.client_type, 
    online: c.online_status, 
    login: c.login_status 
  })))
  
  if (clientId) {
    // 确保选择的客户端在可用列表中
    const isValidClient = availableClients.value.some(c => c.client_id === clientId)
    if (!isValidClient) {
      ElMessage.warning('选择的客户端不可用，请重新选择')
      // 如果当前选择的客户端无效，清空选择
      chatStore.selectClient('')
      chatStore.setSessions([])
      return
    }
    
    chatStore.selectClient(clientId)
    // ✅ 切换客户端时清空当前会话并刷新会话列表
    chatStore.setCurrentSession(null)
    await refreshSessions()
  } else {
    chatStore.selectClient('')
    chatStore.setSessions([])
  }
}

// ✅ 订单记录状态
const currentOrderRecord = ref(null)
const orderButtonLoading = ref(false)

// ✅ 订单按钮文本和类型
const orderButtonText = computed(() => {
  if (!currentOrderRecord.value) {
    return '下单'
  }
  const status = currentOrderRecord.value.order_status
  // ✅ 确保 status 是字符串类型（避免类型不匹配或空值）
  const statusStr = String(status || '').toLowerCase().trim()
  
  console.log('[ChatPanel] orderButtonText computed:', {
    status: status,
    statusStr: statusStr,
    record: currentOrderRecord.value
  })
  
  if (statusStr === 'cancelled' || statusStr === 'failed') {
    return '再次下单'
  }
  // ✅ 待支付状态（ordered）应该显示"取消订单"
  if (statusStr === 'pending' || statusStr === 'ordered' || statusStr === 'paid' || statusStr === 'success') {
    return '取消订单'
  }
  // ✅ 如果状态未知，默认显示"下单"（可能是新订单）
  console.warn('[ChatPanel] 未知订单状态:', statusStr, '订单记录:', currentOrderRecord.value)
  return '下单'
})

const orderButtonType = computed(() => {
  if (!currentOrderRecord.value) {
    return 'primary'
  }
  const status = currentOrderRecord.value.order_status
  // ✅ 确保 status 是字符串类型（避免类型不匹配）
  const statusStr = String(status || '').toLowerCase().trim()
  
  if (statusStr === 'cancelled' || statusStr === 'failed') {
    return 'primary'
  }
  return 'warning'
})

// ✅ 查询订单记录
const checkOrderRecord = async (itemId, sellerId) => {
  if (!itemId) return null
  
  try {
    // ✅ 只使用 item_id 查询，不使用 seller_id（因为数据库中的 seller_id 可能为空或不匹配）
    // 同一个 item_id 通常只有一个订单，所以不需要 seller_id 过滤
    const params = {
      item_id: itemId,
      page_num: 1,
      page_size: 1
    }
    
    const res = await getOrderList(params)
    
    if (res.code === 200 && res.data && res.data.rows && res.data.rows.length > 0) {
      // 返回最新的订单记录（按创建时间倒序）
      const record = res.data.rows[0]
      console.log('[ChatPanel] 找到订单记录:', {
        id: record.id,
        item_id: record.item_id,
        seller_id: record.seller_id,
        order_status: record.order_status,
        order_status_type: typeof record.order_status,
        full_record: record
      })
      
      // ✅ 确保 order_status 字段存在（如果后端返回的字段名不同，尝试转换）
      if (!record.order_status && record.orderStatus) {
        record.order_status = record.orderStatus
      }
      
      return record
    }
    console.log('[ChatPanel] 未找到订单记录: itemId=', itemId, '查询参数:', params, '响应:', res)
    return null
  } catch (error) {
    console.error('[ChatPanel] 查询订单记录失败:', error)
    return null
  }
}

// ✅ 刷新订单记录
const refreshOrderRecord = async () => {
  const session = currentSession.value
  if (!session || !session.itemId) {
    currentOrderRecord.value = null
    return
  }
  
  // ✅ 只使用 itemId 查询，不使用 sellerId（避免 seller_id 不匹配导致查不到记录）
  const orderRecord = await checkOrderRecord(session.itemId, null)
  currentOrderRecord.value = orderRecord
  
  // ✅ 调试日志：显示订单状态和按钮文本
  if (orderRecord) {
    console.log('[ChatPanel] 订单记录状态:', orderRecord.order_status, '按钮文本:', orderButtonText.value, '订单记录:', orderRecord)
  } else {
    console.log('[ChatPanel] 未找到订单记录，按钮文本:', orderButtonText.value)
  }
}

// ✅ 打开商品详情页
const handleOpenItemDetail = () => {
  if (!currentSession.value || !currentSession.value.itemId) {
    ElMessage.warning('缺少商品ID，无法打开详情页')
    return
  }
  
  const itemId = currentSession.value.itemId
  const url = `https://www.goofish.com/item?id=${itemId}`
  window.open(url, '_blank')
  console.log('[ChatPanel] 打开商品详情页:', url)
}

// ✅ 处理下单/取消订单操作
const handleOrderAction = async () => {
  if (!currentSession.value || !currentSession.value.itemId) {
    ElMessage.warning('缺少商品信息，无法操作')
    return
  }
  
  const session = currentSession.value
  orderButtonLoading.value = true
  
  try {
    // 如果已有订单记录且未取消，执行取消订单
    if (currentOrderRecord.value) {
      const status = currentOrderRecord.value.order_status
      // ✅ 确保 status 是字符串类型（避免类型不匹配）
      const statusStr = String(status || '').toLowerCase().trim()
      if (statusStr !== 'cancelled' && statusStr !== 'failed') {
        // 取消订单
        await handleCancelOrder()
        return
      }
    }
    
    // 否则执行下单
    await handlePlaceOrder()
  } catch (error) {
    console.error('[ChatPanel] 操作失败:', error)
    ElMessage.error('操作失败：' + (error.message || '未知错误'))
  } finally {
    orderButtonLoading.value = false
  }
}

// ✅ 下单
const handlePlaceOrder = async () => {
  const session = currentSession.value
  if (!session.itemId) {
    ElMessage.warning('缺少商品ID，无法下单')
    return
  }
  
  try {
    const itemData = {
      item_id: session.itemId,
      title: session.itemTitle || '',
      price: session.itemPrice || '',
      keyword: '' // 聊天框下单不需要关键词
    }
    
    const res = await manualOrder(itemData, { allow_search_accounts: false })
    
    if (res.code === 200) {
      ElMessage.success('下单命令已发送，请等待处理结果...')
      // 刷新订单记录
      await refreshOrderRecord()
    } else {
      ElMessage.error(res.msg || '下单失败')
    }
  } catch (error) {
    console.error('[ChatPanel] 下单失败:', error)
    ElMessage.error('下单失败：' + (error.message || '未知错误'))
  }
}

// ✅ 取消订单
const handleCancelOrder = async () => {
  if (!currentOrderRecord.value || !currentOrderRecord.value.id) {
    ElMessage.warning('订单记录不存在，无法取消')
    return
  }
  
  try {
    const res = await cancelOrder(currentOrderRecord.value.id, '不想买了')
    
    if (res.code === 200) {
      ElMessage.success('取消订单命令已发送，请等待处理结果...')
      // 刷新订单记录
      await refreshOrderRecord()
    } else {
      ElMessage.error(res.msg || '取消订单失败')
    }
  } catch (error) {
    console.error('[ChatPanel] 取消订单失败:', error)
    ElMessage.error('取消订单失败：' + (error.message || '未知错误'))
  }
}

// ✅ 监听消息变化，自动滚动到底部（仅当消息列表更新时）
// ✅ 监听消息变化：直接监听 chatStore.messages，确保实时更新
watch(() => {
  const sessionId = chatStore.currentSessionId
  if (!sessionId) return []
  return chatStore.messages[sessionId] || []
}, async (newMessages, oldMessages) => {
  // 只在消息数量增加时滚动（避免频繁滚动）
  const currentSessionId = chatStore.currentSessionId
  if (currentSessionId && newMessages && newMessages.length > 0) {
    const newCount = newMessages.length
    const oldCount = oldMessages?.length || 0
    if (newCount > oldCount) {
      console.log('[ChatPanel] 检测到新消息，滚动到底部:', {
        sessionId: currentSessionId,
        newCount,
        oldCount
      })
      await scrollToBottom()
    }
  }
}, { deep: true, immediate: false })

// ✅ 监听当前会话变化，自动加载会话消息
watch(() => chatStore.currentSessionId, async (newId, oldId) => {
  if (newId && newId !== oldId) {
    console.log('[ChatPanel] 检测到currentSessionId变化:', { oldId, newId })
    
    // ✅ 显示加载状态
    sessionsLoading.value = true
    
    try {
      // ✅ 检查会话是否存在于会话列表中
      let session = sessions.value.find(s => s.sessionId === newId)
      
      if (session) {
        // ✅ 会话存在，自动加载消息
        console.log('[ChatPanel] 会话已存在，自动加载消息:', newId)
        await loadSessionData(newId)
      } else {
        // ✅ 会话不存在，可能是新创建的，等待会话列表刷新
        console.log('[ChatPanel] 会话不存在，等待刷新会话列表:', newId)
        
        // ✅ 多次尝试刷新（最多3次，每次间隔1秒）
        let retryCount = 0
        const maxRetries = 3
        
        while (retryCount < maxRetries) {
          await refreshSessions(true) // 强制刷新
          
          // 刷新后再次检查
          session = sessions.value.find(s => s.sessionId === newId)
          if (session) {
            console.log('[ChatPanel] 刷新后找到会话，加载消息:', newId)
            await loadSessionData(newId)
            break
          }
          
          retryCount++
          if (retryCount < maxRetries) {
            console.log(`[ChatPanel] 第${retryCount}次刷新未找到会话，等待1秒后重试...`)
            await new Promise(resolve => setTimeout(resolve, 1000))
          }
        }
        
        // ✅ 如果所有重试都失败，显示错误提示
        if (!session) {
          console.error('[ChatPanel] 所有重试后仍未找到会话:', newId)
          ElMessage.error('会话创建失败，请重试')
          // 清空当前会话ID
          chatStore.setCurrentSession(null)
        }
      }
      
      await scrollToBottom()
    } catch (error) {
      console.error('[ChatPanel] 加载会话时出错:', error)
      ElMessage.error('加载会话失败: ' + (error.message || '未知错误'))
    } finally {
      // ✅ 隐藏加载状态
      sessionsLoading.value = false
    }
  }
})

// ✅ 监听聊天窗口打开，自动刷新会话列表并修复"未知卖家"问题
watch(() => chatStore.visible, async (isVisible) => {
  if (isVisible && chatStore.selectedClientId) {
    // 打开聊天窗口时，强制刷新会话列表（不使用缓存，确保显示最新的商品状态）
    await refreshSessions(true)
    
    // ✅ 如果当前有选中的会话，且sellerName为"未知卖家"，尝试获取卖家信息
    if (chatStore.currentSessionId) {
      const session = chatStore.sessions.find(s => s.sessionId === chatStore.currentSessionId)
      if (session && (session.sellerName === '未知卖家' || !session.sellerName) && session.itemId) {
        console.log('[ChatPanel] 检测到"未知卖家"，尝试获取卖家信息:', session)
        try {
          const response = await openChatWithDedup({
            client_id: chatStore.selectedClientId,
            seller_id: session.sellerId,
            item_id: session.itemId,
            seller_name: session.sellerName
          })
          
          if (response.code === 200 && response.data) {
            const updateData = {
              sessionId: session.sessionId,  // ✅ 传递sessionId，确保使用conversation_id（可能包含chat_id）
              sellerId: response.data.seller_id || session.sellerId,
              sellerName: response.data.seller_name || session.sellerName || '未知卖家',
              itemId: session.itemId,
              avatar: response.data.seller_avatar || session.avatar || '',
              itemTitle: response.data.item_title || session.itemTitle || '',
              itemPrice: response.data.item_price || session.itemPrice || '',
              itemImage: response.data.item_image || session.itemImage || '',
              itemTags: response.data.item_tags || session.itemTags || [],
              // ✅ 保存完整的商品详情（用于媒体查看器）
              itemDetail: {
                imageInfos: response.data.image_infos || [],
                videoPlayInfo: response.data.video_play_info || null
              }
            }
            // ✅ 如果API返回了conversation_id，使用它作为sessionId
            if (response.data.conversation_id) {
              updateData.sessionId = response.data.conversation_id
            }
            chatStore.addOrUpdateSession(updateData)
            console.log('[ChatPanel] ✅ 已更新"未知卖家"信息:', updateData)
          }
        } catch (e) {
          console.error('[ChatPanel] 获取卖家信息失败:', e)
        }
      }
    }
  }
})

// ✅ 辅助函数：HTML反转义
const unescapeHTML = (str) => {
  if (!str) return ''
  const temp = document.createElement('div')
  temp.innerHTML = str
  return temp.innerText || temp.textContent
}

// ✅ 格式化系统消息内容
const formatSystemContent = (msg) => {
  let content = ''
  if (msg.system_data) {
    content = msg.system_data.content || msg.system_data.title || ''
  } else {
    content = msg.message_content || ''
  }
  // 如果内容包含 HTML 标签的转义字符（如 &lt;），先反转义
  if (content.includes('&lt;') || content.includes('&quot;')) {
    return unescapeHTML(content)
  }
  return content
}

// ✅ 滚动消息到底部的辅助函数
const scrollToBottom = async () => {
  await nextTick()
  // 等待多个渲染周期，确保DOM完全更新
  await nextTick()
  const wrap = messageScrollRef.value
  if (wrap && wrap.wrapRef) {
    const el = wrap.wrapRef
    // 使用 requestAnimationFrame 确保DOM更新完成
    requestAnimationFrame(() => {
      // 滚动到最底部（显示最新消息）
      el.scrollTop = el.scrollHeight
      // 如果第一次滚动不够，再尝试一次（确保滚动到底部）
      setTimeout(() => {
        el.scrollTop = el.scrollHeight
      }, 50)
    })
  }
}

// ✅ 处理滚动穿透
const handleWheel = (e) => {
  const wrap = messageScrollRef.value?.wrapRef
  if (!wrap) return
  
  const { scrollTop, scrollHeight, clientHeight } = wrap
  const isAtTop = scrollTop === 0
  const isAtBottom = Math.abs(scrollHeight - clientHeight - scrollTop) < 1
  
  // 如果在顶部向上滚动，或在底部向下滚动，阻止事件冒泡和默认行为
  if ((isAtTop && e.deltaY < 0) || (isAtBottom && e.deltaY > 0)) {
    e.preventDefault()
    e.stopPropagation()
  }
}

// ✅ 处理滚动事件（加载更多）
const handleScroll = async ({ scrollTop }) => {
  // 滚动到顶部，加载更多历史消息
  if (scrollTop === 0 && !loadingMoreMessages.value && hasMoreMessages.value[chatStore.currentSessionId] !== false) {
    await loadMoreMessages()
  }
}

// ✅ 加载更多历史消息
const loadMoreMessages = async () => {
  const sessionId = chatStore.currentSessionId
  if (!sessionId || loadingMoreMessages.value) return
  
  const session = chatStore.sessions.find(s => s.sessionId === sessionId)
  if (!session) return
  
  // 初始化页码
  if (!messagePageNum.value[sessionId]) {
    messagePageNum.value[sessionId] = 1
  }
  
  // 检查是否还有更多消息
  if (hasMoreMessages.value[sessionId] === false) {
    console.log('[ChatPanel] 没有更多历史消息了')
    return
  }
  
  loadingMoreMessages.value = true
  const nextPage = messagePageNum.value[sessionId] + 1
  
  try {
    console.log(`[ChatPanel] 加载更多历史消息，第${nextPage}页...`)
    
    // 记录当前滚动位置和高度
    const wrap = messageScrollRef.value
    const el = wrap?.wrapRef
    const oldScrollHeight = el?.scrollHeight || 0
    
    // ✅ 构建查询参数
    const requestParams = {
      client_id: chatStore.selectedClientId,
      page_num: nextPage,
      page_size: 50
    }
    
    // ✅ 优先使用 conversation_id 查询（如果不包含#号，说明是真实的会话ID）
    // 这样可以查询到 item_id 为空的发送消息（解决"我看不到自己发送的消息"问题）
    if (sessionId && !String(sessionId).includes('#')) {
      requestParams.conversation_id = sessionId
    } else {
      // 降级：使用 seller_id + item_id 查询
      requestParams.seller_id = session.sellerId
      requestParams.item_id = session.itemId
    }
    
    const res = await request.get('/agent/chat/messages', {
      params: requestParams
    })
    
    if (res.code === 200 && res.data && res.data.rows) {
      const newMessages = res.data.rows.map(msg => ({
        messageId: msg.message_id,
        tempId: msg.temp_id,
        mid: msg.mid,
        direction: msg.direction,
        message_content: msg.message_content,
        message_type: msg.message_type,
        content_type: msg.content_type,
        custom_content_type: msg.custom_content_type,
        message_time: msg.message_time,
        read_status: msg.read_status,
        message_status: msg.message_status,
        image_url: msg.image_url,
        image_width: msg.image_width,
        image_height: msg.image_height,
        voice_url: msg.voice_url,
        voice_duration: msg.voice_duration,
        voice_size_bytes: msg.voice_size_bytes,
        video_url: msg.video_url,
        video_cover: msg.video_cover,
        video_duration: msg.video_duration,
        system_data: msg.system_data
      }))
      
      if (newMessages.length > 0) {
        // 合并到现有消息
        const existingMessages = chatStore.messages[sessionId] || []
        const existingMessageIds = new Set(existingMessages.map(m => m.messageId || m.localId).filter(Boolean))
        const filteredNewMessages = newMessages.filter(msg => !existingMessageIds.has(msg.messageId))
        
        if (filteredNewMessages.length > 0) {
          const allMessages = [...filteredNewMessages, ...existingMessages].sort((a, b) => {
            const timeA = new Date(a.message_time).getTime()
            const timeB = new Date(b.message_time).getTime()
            return timeA - timeB
          })
          
          chatStore.setMessages(sessionId, allMessages)
          messagePageNum.value[sessionId] = nextPage
          
          // 恢复滚动位置（保持在原来的位置，不要跳到顶部或底部）
          await nextTick()
          if (el) {
            const newScrollHeight = el.scrollHeight
            el.scrollTop = newScrollHeight - oldScrollHeight
          }
          
          console.log(`[ChatPanel] 成功加载${filteredNewMessages.length}条历史消息`)
        } else {
          console.log('[ChatPanel] 没有新的历史消息（已存在）')
        }
      }
      
      // 判断是否还有更多
      if (newMessages.length < 50) {
        hasMoreMessages.value[sessionId] = false
        console.log('[ChatPanel] 已加载所有历史消息')
      }
    } else {
      hasMoreMessages.value[sessionId] = false
    }
  } catch (error) {
    console.error('[ChatPanel] 加载更多历史消息失败:', error)
    ElMessage.error('加载历史消息失败')
  } finally {
    loadingMoreMessages.value = false
  }
}

const handleSelectSession = async (sessionId) => {
  if (sessionId === chatStore.currentSessionId) {
    // 如果是当前会话，手动刷新
    await loadSessionData(sessionId)
  } else {
    // 如果是新会话，设置ID（会触发watcher调用loadSessionData）
    chatStore.setCurrentSession(sessionId)
  }
}

const loadSessionData = async (sessionId) => {
  const isSameSession = sessionId === chatStore.currentSessionId
  
  console.log('[ChatPanel] 🔍 加载会话数据:', {
    sessionId,
    currentSessionId: chatStore.currentSessionId,
    isSame: isSameSession,
    hasMessages: (chatStore.messages[sessionId] || []).length
  })
  
  // ✅ 如果点击的是当前会话，且消息已加载，直接返回（避免重复发送已读回执）
  // 注意：如果是由watcher触发（loadSessionData），isSameSession必定为true（因为ID已设置）
  // 所以这里要小心，如果是watcher触发的首次加载，messages长度可能为0，不会返回
  if (isSameSession && (chatStore.messages[sessionId] || []).length > 0) {
    console.log('[ChatPanel] ⚠️ 已是当前会话且消息已加载，跳过重复操作')
    return
  }
  
  // ✅ 清空语音播放状态（切换会话时）
  if (playingVoiceMessageId.value) {
    const audioElements = Array.isArray(voiceAudioRefs.value) ? voiceAudioRefs.value : []
    const currentAudio = audioElements.find(el => el?.dataset?.messageId === playingVoiceMessageId.value)
    if (currentAudio) {
      currentAudio.pause()
      currentAudio.currentTime = 0
    }
    playingVoiceMessageId.value = null
  }
  // ✅ 清空audio refs数组（切换会话时重新收集）
  voiceAudioRefs.value = []
  
  // ✅ 初始化历史消息加载状态
  if (!messagePageNum.value[sessionId]) {
    messagePageNum.value[sessionId] = 1
    hasMoreMessages.value[sessionId] = true
  }
  
  // ✅ 重置图片预览索引（切换会话时恢复显示主图）
  currentPreviewIndex.value = 0
  
  // ✅ 查询订单记录
  await refreshOrderRecord()
  
  // ✅ 清除未读数并发送已读回执
  const session = sessions.value.find(s => s.sessionId === sessionId)
  
  // ✅ 无论unread计数是否为0，都尝试发送已读回执（因为刷新后unread可能被重置为0，但消息实际未读）
  // 获取当前会话的所有接收消息ID列表（用于发送已读回执）
  const currentMessages = chatStore.messages[sessionId] || []
  
  // ✅ 筛选出接收方向（recv）的消息，提取messageId
  // 注意：messageId格式应该为 "3808247424509.PNM"（Xianyu格式）
  // 只发送格式正确的消息ID（包含点号的，表示是Xianyu的消息ID）
  // ✅ 重要：只处理接收方向的消息，发送方向的消息不应该发送已读回执
  // ✅ 重要：排除已读消息（read_status === 2），避免重复发送已读回执
  
  // 统计消息数量（用于调试）
  const messageStats = {
    total: currentMessages.length,
    recv: currentMessages.filter(m => m.direction === 'recv').length,
    alreadyRead: currentMessages.filter(m => m.direction === 'recv' && (m.read_status === 2 || m.readStatus === 2)).length,
    invalidId: currentMessages.filter(m => m.direction === 'recv' && (!m.messageId || !m.messageId.includes('.'))).length
  }
  
  const recvMessageIds = currentMessages
    .filter(msg => {
      // ✅ 只处理接收方向的消息（排除发送方向的消息）
      if (msg.direction !== 'recv') {
        // 如果是发送方向的消息，跳过（不应该发送已读回执）
        return false
      }
      // ✅ 排除已读消息（read_status === 2），只处理未读消息
      if (msg.read_status === 2 || msg.readStatus === 2) {
        // 如果消息已经标记为已读，跳过（不重复发送已读回执）
        return false
      }
      // 必须有messageId且格式正确（包含点号，如 "3808247424509.PNM"）
      if (!msg.messageId || typeof msg.messageId !== 'string') return false
      // 必须包含点号（Xianyu消息ID格式）
      if (!msg.messageId.includes('.')) return false
      return true
    })
    .map(msg => msg.messageId)
    .slice(0, 50) // 限制最多50条，避免请求过大
  
  console.log('[ChatPanel] 📊 已读回执筛选统计:', {
    ...messageStats,
    needMarkRead: recvMessageIds.length,
    sessionId
  })
  
  // ✅ 如果unread > 0，清除未读数
  if (session && session.unread > 0) {
    const unreadCount = session.unread
    session.unread = 0
    // ✅ 调整总未读数
    chatStore.decrementUnread(unreadCount)
  }
  
  // ✅ 发送已读回执到后端（标记消息为已读）
  // 只要有接收方向的消息且格式正确，就发送已读回执（不管unread计数）
  // ⚠️ 跳过已下架商品的会话，避免"数据正在处理，请勿重复提交"错误
  // ⚠️ 使用锁机制避免重复发送
  const isDelisted = session.itemStatus === 'deleted' || session.itemStatus === -2 || session.itemStatus === '-2'
  if (recvMessageIds.length > 0 && chatStore.selectedClientId && !isDelisted) {
    // 检查是否已有正在发送的请求
    if (markReadLocks.has(sessionId)) {
      console.log('[ChatPanel] ⚠️ 已有正在发送的已读回执，跳过重复请求:', sessionId)
      // 等待已有请求完成
      try {
        await markReadLocks.get(sessionId)
      } catch (e) {
        // 忽略错误，继续执行
      }
    } else {
      // 创建新的已读回执请求
      const markReadPromise = (async () => {
        try {
          await markChatRead({
            client_id: chatStore.selectedClientId,
            seller_id: session.sellerId,
            item_id: session.itemId,
            message_ids: recvMessageIds
          })
          console.log('[ChatPanel] ✅ 已读回执已发送:', recvMessageIds.length, '条消息', 'message_ids:', recvMessageIds.slice(0, 5))
          
          // ✅ 更新本地消息的read_status为已读（2），避免重复发送
          const messages = chatStore.messages[sessionId] || []
          messages.forEach(msg => {
            if (recvMessageIds.includes(msg.messageId)) {
              msg.read_status = 2
              msg.readStatus = 2
            }
          })
        } catch (e) {
          console.error('[ChatPanel] 发送已读回执失败:', e)
          throw e
        } finally {
          // 请求完成后移除锁
          markReadLocks.delete(sessionId)
        }
      })()
      
      // 保存Promise到锁Map中
      markReadLocks.set(sessionId, markReadPromise)
    }
  } else if (isDelisted) {
    console.log('[ChatPanel] 跳过已下架/删除商品的已读回执发送:', session.itemStatus)
  } else {
    if (!chatStore.selectedClientId) {
      console.log('[ChatPanel] 无需发送已读回执（未选择客户端）')
    } else if (recvMessageIds.length === 0) {
      console.log('[ChatPanel] 无需发送已读回执（无接收消息或消息ID格式不正确）', {
        totalMessages: currentMessages.length,
        recvMessages: currentMessages.filter(m => m.direction === 'recv').length,
        validMessageIds: recvMessageIds.length
      })
    }
  }
  
  // 加载该会话的历史消息
  if (!session) {
    console.warn('[ChatPanel] 未找到会话:', sessionId)
    return
  }
  
  console.log('[ChatPanel] 加载会话消息:', session)
  
  // ✅ 如果会话有itemId但商品信息不完整，尝试通过后端API获取商品详情
  // 检查条件：缺少关键商品信息（图片、标题、价格）就重新获取
  // ⚠️ 但跳过已下架的商品，避免重复调用导致"请勿重复提交"错误
  // ⚠️ itemDetail 不作为必须条件，只在真正需要时（点击主图/缩略图时）才按需加载
  const isDelistedItem = session.itemStatus === 'deleted' || session.itemStatus === -2 || session.itemStatus === '-2'
  
  // ✅ 总是获取最新的商品信息（确保状态、价格、下架状态等是实时的）
  const shouldRefreshItemInfo = session.itemId && chatStore.selectedClientId
  
  if (shouldRefreshItemInfo) {
    try {
      console.log('[ChatPanel] 正在刷新商品信息:', {
        itemId: session.itemId,
        currentStatus: session.itemStatusStr
      })
      
      // ✅ 使用会话的sellerId（可能是加密的，后端会解析）
      const response = await openChatWithDedup({
        client_id: chatStore.selectedClientId,
        seller_id: session.sellerId, // 后端会通过item_id获取纯数字seller_id
        item_id: session.itemId,
        seller_name: session.sellerName
      })
      
      if (response.code === 200 && response.data) {
        // ✅ 更新会话信息（包括seller_id、头像、商品信息）
        const updatedSellerId = response.data.seller_id || session.sellerId
        
        // ✅ 构建完整的商品信息更新对象
        const updateData = {
          sellerId: updatedSellerId, // 使用后端返回的纯数字seller_id
          sellerName: response.data.seller_name || session.sellerName || '未知卖家',
          itemId: session.itemId,
          avatar: response.data.seller_avatar || session.avatar || '',
          itemTitle: response.data.item_title || session.itemTitle || '',
          itemPrice: response.data.item_price || session.itemPrice || '',
          itemImage: response.data.item_image || session.itemImage || '',
          itemTags: response.data.item_tags || session.itemTags || [],
          itemStatus: response.data.item_status, // ✅ 确保更新商品状态
          itemStatusStr: response.data.item_status_str, // ✅ 确保更新商品状态描述
          // ✅ 保存完整的商品详情（用于媒体查看器）
          itemDetail: {
            imageInfos: response.data.image_infos || [],
            videoPlayInfo: response.data.video_play_info || null
          }
        }
        
        // ✅ 更新会话（这会自动更新store中的会话信息）
        // ✅ 重要：传递conversation_id作为sessionId，确保使用正确的会话ID
        if (response.data.conversation_id) {
          updateData.sessionId = response.data.conversation_id
        }
        chatStore.addOrUpdateSession(updateData)
        
        // ✅ 如果seller_id更新，但sessionId不应该改变（应该保持conversation_id）
        // 注意：sessionId应该使用conversation_id（可能是chat_id），不应该重新构建
        // 如果seller_id更新了，只需要更新会话信息，不需要改变sessionId
        // 因为conversation_id是唯一的会话标识，不会因为seller_id更新而改变
        
        console.log('[ChatPanel] ✅ 已更新会话信息:', {
          seller_id: updatedSellerId,
          seller_name: updateData.sellerName,
          avatar: updateData.avatar,
          item_image: updateData.itemImage,
          item_title: updateData.itemTitle,
          item_price: updateData.itemPrice,
          item_tags: updateData.itemTags
        })
        
        // ✅ 如果seller_id更新了，需要重新加载消息（使用新的seller_id）
        if (updatedSellerId !== session.sellerId) {
          session.sellerId = updatedSellerId
          // 重新加载消息
          try {
            const res = await getChatMessages({
              client_id: chatStore.selectedClientId,
              seller_id: updatedSellerId,
              item_id: session.itemId,
              page_num: 1,
              page_size: 50
            })
            if (res.code === 200 && res.data && res.data.rows) {
              const sortedMessages = res.data.rows
                .map(msg => {
                  // ✅ 解析message_extra JSON（如果存在）
                  let messageExtra = null
                  if (msg.message_extra) {
                    try {
                      messageExtra = typeof msg.message_extra === 'string' ? JSON.parse(msg.message_extra) : msg.message_extra
                    } catch (e) {
                      console.warn('[ChatPanel] 解析message_extra失败:', e, msg.message_extra)
                    }
                  }
                  
                  return {
                    messageId: msg.message_id,
                    direction: msg.direction,
                    message_content: msg.message_content,
                    message_time: msg.message_time,
                    message_type: msg.message_type || 'text',
                    read_status: msg.read_status || 1,  // ✅ 已读状态（1=未读，2=已读）
                    readStatus: msg.read_status || 1,   // ✅ 兼容字段
                    // ✅ 提取并存储图片信息
                    image_url: messageExtra?.image_url || null,
                    image_width: messageExtra?.image_width || null,
                    image_height: messageExtra?.image_height || null,
                    // ✅ 提取并存储语音信息
                    voice_url: messageExtra?.voice_url || null,
                    voice_duration: messageExtra?.voice_duration || null,
                    voice_size_bytes: messageExtra?.voice_size_bytes || null,
                  }
                })
                .sort((a, b) => {
                  const timeA = new Date(a.message_time).getTime()
                  const timeB = new Date(b.message_time).getTime()
                  return timeA - timeB
                })
              
              // ✅ 合并历史消息和已有消息（避免覆盖通过WebSocket接收的实时消息）
              // ✅ 使用正确的sessionId（应该是当前会话ID，而不是newSessionId）
              const currentSessionId = chatStore.currentSessionId || sessionId
              const existingMessages = chatStore.messages[currentSessionId] || []
              const existingMessageIds = new Set(existingMessages.map(m => m.messageId || m.localId).filter(Boolean))
              
              // ✅ 数据库去重：对sortedMessages去重
              const msgMap = new Map()
              sortedMessages.forEach(msg => {
                if (msg.messageId) {
                  const existing = msgMap.get(msg.messageId)
                  if (!existing || new Date(msg.message_time).getTime() > new Date(existing.message_time).getTime()) {
                    msgMap.set(msg.messageId, msg)
                  }
                }
              })
              const uniqueSorted = Array.from(msgMap.values())
              
              const newMessages = uniqueSorted.filter(msg => {
                const msgId = msg.messageId
                return !msgId || !existingMessageIds.has(msgId)
              })
              
              const allMessages = [...existingMessages, ...newMessages].sort((a, b) => {
                const timeA = new Date(a.message_time || a.timestamp || 0).getTime()
                const timeB = new Date(b.message_time || b.timestamp || 0).getTime()
                return timeA - timeB
              })
              
              chatStore.setMessages(currentSessionId, allMessages)
              // console.log(`[ChatPanel] 重新加载消息后，合并后共 ${allMessages.length} 条消息（历史 ${sortedMessages.length} 条 + 实时 ${existingMessages.length} 条）`) // ✅ 已注释：日志太多
            }
          } catch (e) {
            console.error('[ChatPanel] 重新加载消息失败:', e)
          }
        }
      } else {
        console.warn('[ChatPanel] 获取商品信息失败，响应:', response)
      }
    } catch (e) {
      console.error('[ChatPanel] 获取商品信息失败:', e)
    }
  }
  
  try {
    // ✅ 使用会话的sellerId查询消息（确保使用纯数字ID）
    // ✅ 确保使用正确的sessionId（可能是更新后的）
    const finalSessionId = chatStore.currentSessionId || sessionId
    const res = await getChatMessages({
      client_id: chatStore.selectedClientId,
      seller_id: session.sellerId, // 使用更新后的seller_id
      item_id: session.itemId,
      page_num: 1,
      page_size: 50
    })
    
    // console.log('[ChatPanel] 消息历史响应:', res) // ✅ 已注释：日志太多
    
    if (res.code === 200 && res.data && res.data.rows) {
      // ✅ 将消息按时间正序排序（旧消息在前，新消息在后）
      // 后端可能返回倒序，需要前端排序确保一致性
      const sortedMessages = res.data.rows
        .map((msg, index) => {
          // ✅ 解析message_extra JSON（如果存在）
          let messageExtra = null
          if (msg.message_extra) {
            try {
              messageExtra = typeof msg.message_extra === 'string' ? JSON.parse(msg.message_extra) : msg.message_extra
            } catch (e) {
              console.warn('[ChatPanel] 解析message_extra失败:', e, msg.message_extra)
            }
          }
          
          // ✅ 调试日志：打印消息解析结果（仅对前3条消息）
          if (index < 3) {
            // console.log(`[ChatPanel] 消息${index + 1}解析结果:`, {
            //   message_id: msg.message_id,
            //   message_type: msg.message_type,
            //   message_content: msg.message_content,
            //   message_extra_raw: msg.message_extra,
            //   message_extra_parsed: messageExtra,
            //   image_url: messageExtra?.image_url,
            //   voice_url: messageExtra?.voice_url
            // }) // ✅ 已注释：日志太多
          }
          
          const messageObj = {
            messageId: msg.message_id,
            direction: msg.direction,
            message_content: msg.message_content,
            message_time: msg.message_time,
            message_type: msg.message_type || 'text',
            read_status: msg.read_status || 1,  // ✅ 已读状态（1=未读，2=已读）
            readStatus: msg.read_status || 1,   // ✅ 兼容字段
            // ✅ 提取并存储回复消息ID
            reply_message_id: messageExtra?.reply_message_id || null,
            // ✅ 提取并存储图片信息（从message_extra中提取）
            image_url: messageExtra?.image_url || null,
            image_width: messageExtra?.image_width || null,
            image_height: messageExtra?.image_height || null,
            // ✅ 提取并存储语音信息（从message_extra中提取）
            voice_url: messageExtra?.voice_url || null,
            voice_duration: messageExtra?.voice_duration || null,
            voice_size_bytes: messageExtra?.voice_size_bytes || null,
            // ✅ 提取并存储系统消息数据（从message_extra中提取）
            system_data: messageExtra?.system_data || null,
          }
          
          // ✅ 调试日志：如果消息类型是voice或image但没有对应的URL，打印警告
          if (messageObj.message_type === 'voice' && !messageObj.voice_url) {
            console.warn('[ChatPanel] ⚠️ 语音消息缺少voice_url:', messageObj, 'message_extra:', messageExtra)
          }
          if (messageObj.message_type === 'image' && !messageObj.image_url) {
            console.warn('[ChatPanel] ⚠️ 图片消息缺少image_url:', messageObj, 'message_extra:', messageExtra)
          }
          
          return messageObj
        })
        .sort((a, b) => {
          // 按时间正序排序（旧的在前面，新的在后面）
          const timeA = new Date(a.message_time).getTime()
          const timeB = new Date(b.message_time).getTime()
          return timeA - timeB
        })
      
      // ✅ 合并历史消息和已有消息（避免覆盖通过WebSocket实时收到的消息）
      const existingMessages = chatStore.messages[finalSessionId] || []
      const existingMessageIds = new Set(existingMessages.map(m => m.messageId || m.localId).filter(Boolean))
      
      // ✅ 数据库去重：首先对 sortedMessages 自身进行去重（防止数据库中有重复记录）
      const messageMap = new Map()
      sortedMessages.forEach(msg => {
        const msgId = msg.messageId
        if (msgId) {
          // 如果消息ID已存在，保留时间更新的那条
          const existing = messageMap.get(msgId)
          if (!existing || new Date(msg.message_time).getTime() > new Date(existing.message_time).getTime()) {
            messageMap.set(msgId, msg)
          }
        } else {
          // 如果没有消息ID，使用时间戳作为临时key（理论上不应该出现）
          messageMap.set(`temp_${Date.now()}_${Math.random()}`, msg)
        }
      })
      const uniqueSortedMessages = Array.from(messageMap.values())
      
      // 只添加不存在的消息（避免重复）
      const newMessages = uniqueSortedMessages.filter(msg => {
        const msgId = msg.messageId
        return !msgId || !existingMessageIds.has(msgId)
      })
      
      // 如果有新消息，合并到已有消息中
      if (newMessages.length > 0 || existingMessages.length === 0) {
        // 合并所有消息并按时间排序
        const allMessages = [...existingMessages, ...newMessages].sort((a, b) => {
          const timeA = new Date(a.message_time || a.timestamp || 0).getTime()
          const timeB = new Date(b.message_time || b.timestamp || 0).getTime()
          return timeA - timeB
        })
        
        chatStore.setMessages(finalSessionId, allMessages)
        // console.log(`[ChatPanel] 已加载 ${sortedMessages.length} 条历史消息，合并后共 ${allMessages.length} 条消息（会话 ${finalSessionId}）`) // ✅ 已注释：日志太多
      } else {
        // 如果已有消息且没有新消息，只更新时间戳确保排序正确
        const allMessages = [...existingMessages].sort((a, b) => {
          const timeA = new Date(a.message_time || a.timestamp || 0).getTime()
          const timeB = new Date(b.message_time || b.timestamp || 0).getTime()
          return timeA - timeB
        })
        chatStore.setMessages(finalSessionId, allMessages)
        // console.log(`[ChatPanel] 使用已有消息 ${existingMessages.length} 条，未加载新的历史消息`) // ✅ 已注释：日志太多
      }
      
      // ✅ 确保当前会话ID正确
      if (chatStore.currentSessionId !== finalSessionId) {
        chatStore.setCurrentSession(finalSessionId)
      }
      
      // ✅ 加载完消息后滚动到底部（显示最新消息）
      await nextTick()
      await scrollToBottom()
    } else {
      // ✅ 即使没有历史消息，也保留已有的实时消息（通过WebSocket接收的）
      const existingMessages = chatStore.messages[finalSessionId] || []
      if (existingMessages.length > 0) {
        // console.log(`[ChatPanel] 没有历史消息，但保留已有实时消息 ${existingMessages.length} 条`) // ✅ 已注释：日志太多
        chatStore.setMessages(finalSessionId, existingMessages)
      } else {
        chatStore.setMessages(finalSessionId, [])
        // console.log('[ChatPanel] 该会话暂无历史消息') // ✅ 已注释：日志太多
      }
    }
  } catch (error) {
    console.error('[ChatPanel] 加载消息历史失败:', error)
    // 初始化为空数组，避免无法显示输入框
    const finalSessionId = chatStore.currentSessionId || sessionId
    chatStore.setMessages(finalSessionId, [])
    ElMessage.error('加载消息历史失败: ' + (error.message || '未知错误'))
  }
}

// ✅ 加载淘宝表情包
const loadTaobaoEmojis = async () => {
  if (taobaoEmojis.value.length > 0) {
    return // 已加载，不重复加载
  }
  
  // 检查是否选择了客户端
  if (!chatStore.selectedClientId) {
    console.warn('[ChatPanel] 未选择客户端，无法加载表情包')
    ElMessage.warning('请先选择客户端后再使用表情包功能')
    return
  }
  
  emojiLoading.value = true
  try {
    // 发送请求到Agent加载表情包（通过WebSocket）
    websocketManager.send({
      event: 'load_emoji',
      direction: 'req',
      data: {
        client_id: chatStore.selectedClientId
      }
    })
    
    // 注意：表情包数据会通过WebSocket的响应返回，这里只是发送请求
    // 实际的表情包数据会在WebSocket消息处理中更新taobaoEmojis
    console.log('[ChatPanel] 已请求加载表情包')
  } catch (error) {
    console.error('[ChatPanel] 加载表情包失败:', error)
    ElMessage.error('加载表情包失败：' + (error.message || '未知错误'))
  } finally {
    emojiLoading.value = false
  }
}

// ✅ 插入表情到输入框
const insertEmoji = (emoji) => {
  if (!messageInputRef.value) return
  
  // 表情包格式：使用表情包的code或URL
  // 这里使用一个标记格式，后端可以解析并转换为实际的表情包
  const emojiText = `[emoji:${emoji.code || emoji.url}]`
  
  const textarea = messageInputRef.value.$el?.querySelector('textarea')
  if (textarea) {
    const start = textarea.selectionStart
    const end = textarea.selectionEnd
    const text = draftMessage.value
    const newText = text.substring(0, start) + emojiText + text.substring(end)
    draftMessage.value = newText
    
    // 恢复光标位置
    nextTick(() => {
      const newPos = start + emojiText.length
      textarea.setSelectionRange(newPos, newPos)
      textarea.focus()
    })
  } else {
    // 如果找不到textarea，直接追加
    draftMessage.value += emojiText
  }
  
  // 点击表情后不关闭选择器，方便连续选择
}

// ✅ 上传图片到闲鱼服务器（通过后端代理，避免CORS问题）
const uploadImage = async (file) => {
  try {
    // ✅ 检查文件大小（限制50MB，使用文件传输服务器，不压缩）
    const MAX_FILE_SIZE = 50 * 1024 * 1024  // 50MB
    if (file.size > MAX_FILE_SIZE) {
      throw new Error(`图片大小超过限制（最大${MAX_FILE_SIZE / 1024 / 1024}MB），当前${(file.size / 1024 / 1024).toFixed(2)}MB`)
    }
    
    const formData = new FormData()
    formData.append('file', file, file.name || 'pasteImg0')
    
    // ✅ 获取当前选中的client_id，用于后端获取Agent的Cookie
    const clientId = chatStore.selectedClientId
    if (!clientId) {
      throw new Error('请先选择聊天客户端')
    }
    
    // ✅ 获取当前会话的conversation_id（优先使用sessionId，可能包含chat_id）
    const session = currentSession.value
    if (!session) {
      throw new Error('请先选择会话')
    }
    
    // ✅ 优先使用sessionId（可能包含chat_id），如果没有则构建seller_id#item_id
    let conversationId = session.sessionId
    if (!conversationId) {
      const sellerId = session.sellerId || ''
      const itemId = session.itemId || ''
      conversationId = `${sellerId}#${itemId}`
      
      // ✅ 确保conversation_id不为空（如果sellerId和itemId都为空，使用临时值）
      if (!sellerId && !itemId) {
        conversationId = 'temp#temp'  // 临时值，避免422错误
      }
    }
    
    formData.append('conversation_id', conversationId)
    // ✅ 可选：也添加seller_id和item_id作为备用（如果conversation_id解析失败）
    const sellerId = session.sellerId || ''
    const itemId = session.itemId || ''
    if (sellerId) {
      formData.append('seller_id', sellerId)
    }
    if (itemId) {
      formData.append('item_id', itemId)
    }
    
    // ✅ 生成临时ID和mid（用于乐观更新匹配）
    const tempId = generateTempId()
    const mid = generateMid()
    formData.append('temp_id', tempId)
    formData.append('mid', mid)
    
    // ✅ 构建URL，如果存在client_id则添加到查询参数
    let uploadUrl = '/agent/chat/upload_image'
    if (clientId) {
      uploadUrl += `?client_id=${encodeURIComponent(clientId)}`
    }
    
    // ✅ 使用统一的request工具，确保路径和认证正确
    // axios会自动处理FormData，不需要手动设置Content-Type
    const result = await request({
      url: uploadUrl,
      method: 'post',
      data: formData
      // ✅ 不设置Content-Type，让axios自动处理multipart/form-data
    })
    
    if (result.code === 200 && result.data) {
      return {
        url: result.data.url,
        width: result.data.width || null,
        height: result.data.height || null,
        tempId: tempId,  // ✅ 返回临时ID用于乐观更新
        mid: mid  // ✅ 返回mid用于匹配
      }
    } else {
      throw new Error(result.msg || '上传失败')
    }
  } catch (error) {
    console.error('[ChatPanel] 图片上传失败:', error)
    throw error
  }
}

// ✅ 处理粘贴事件（支持图片和表情）
const handlePaste = async (event) => {
  const clipboardData = event.clipboardData || window.clipboardData
  if (!clipboardData) return
  
  // 检查是否有图片
  const items = clipboardData.items
  for (let i = 0; i < items.length; i++) {
    const item = items[i]
    
    if (item.type.indexOf('image') !== -1) {
      event.preventDefault() // 阻止默认粘贴行为
      
      const file = item.getAsFile()
      if (file) {
        // ✅ 保存当前会话ID，防止上传过程中切换会话导致图片发送到错误的会话
        const currentSessionId = chatStore.currentSessionId
        if (!currentSessionId) {
          ElMessage.warning('请先选择会话')
          return
        }
        
        // ✅ 直接上传图片并发送，不插入到输入框
        try {
          ElMessage.info('正在上传图片...')
          const imageInfo = await uploadImage(file)
          
          // ✅ 上传完成后，检查是否还在同一个会话（防止用户在上传过程中切换会话）
          if (chatStore.currentSessionId !== currentSessionId) {
            ElMessage.warning('会话已切换，图片未发送')
            return
          }
          
          // ✅ 乐观更新：立即在UI上显示图片消息
          const session = currentSession.value
          if (session) {
            const optimisticMsg = {
              tempId: imageInfo.tempId,  // ✅ 使用上传时生成的临时ID
              mid: imageInfo.mid,  // ✅ 用于匹配官方响应
              direction: 'send',
              message_type: 'image',
              message_content: '[图片]',
              image_url: imageInfo.url,
              image_width: imageInfo.width,
              image_height: imageInfo.height,
              message_time: new Date().toISOString(),
              messageStatus: MessageStatus.PENDING,  // ✅ 使用状态机
              pending: true  // 标记为等待确认
            }
            
            // ✅ 使用 appendMessages 直接添加到当前会话
            chatStore.appendMessages(currentSessionId, [optimisticMsg])
            console.log('[ChatPanel] ✅ 图片消息已添加到UI:', optimisticMsg)
            
            // ✅ 设置超时检查 (30秒)
            setTimeout(() => {
              const msg = chatStore.findMessageByTempId(session.sessionId, imageInfo.tempId)
              if (msg && msg.pending) {
                console.warn('[ChatPanel] 图片消息发送超时:', imageInfo.tempId)
                chatStore.updateMessageByTempId(session.sessionId, imageInfo.tempId, {
                  messageStatus: MessageStatus.FAILED,
                  pending: false,
                  error: '发送超时，请重试'
                })
              }
            }, 30000)
            
            // ✅ 滚动到底部显示新消息
            await nextTick()
            await scrollToBottom()
          }
          
          ElMessage.success('图片已发送')
        } catch (error) {
          console.error('[ChatPanel] 处理粘贴图片失败:', error)
          ElMessage.error('图片上传失败: ' + (error.message || '未知错误'))
        }
      }
      return
    }
  }
  
  // 如果不是图片，允许默认粘贴行为（文本）
}

// ✅ 处理图片点击（调试用）
const handleImageClick = (imageUrl) => {
  console.log('[ChatPanel] 图片点击:', imageUrl)
  // 如果Element Plus的预览有问题，可以在新窗口打开
  // window.open(imageUrl, '_blank')
}

// ✅ 发送图片消息
const sendImageMessage = async (imageUrl, imageWidth, imageHeight) => {
  const session = currentSession.value
  if (!session) {
    ElMessage.warning('请先选择会话')
    return
  }
  
  // ✅ 保存当前会话ID，防止发送过程中切换会话
  const currentSessionId = session.sessionId
  
  // ✅ 乐观更新：立即显示图片消息
  const optimisticMsg = {
    localId: `local-image-${Date.now()}`,
    direction: 'send',
    message_type: 'image',
    message_content: '[图片]',
    image_url: imageUrl,
    image_width: imageWidth,
    image_height: imageHeight,
    message_time: new Date().toISOString(),
    pending: true,
    status: 'sending'
  }
  chatStore.appendMessages(currentSessionId, [optimisticMsg])
  
  // ✅ 更新会话的最后消息（传递sessionId，确保使用正确的conversation_id）
  chatStore.addOrUpdateSession({
    sessionId: session.sessionId,  // ✅ 传递sessionId，确保使用conversation_id（可能包含chat_id）
    sellerId: session.sellerId,
    itemId: session.itemId,
    lastMessage: '[图片]',
    lastTime: new Date().toISOString()
  })
  
  try {
    // ✅ 发送前再次检查是否还在同一个会话（防止用户在上传完成后、发送前切换会话）
    if (chatStore.currentSessionId !== currentSessionId) {
      // ✅ 如果会话已切换，移除乐观更新的消息
      const msgList = chatStore.messages[currentSessionId]
      if (msgList) {
        const msgIndex = msgList.findIndex(m => m.localId === optimisticMsg.localId)
        if (msgIndex !== -1) {
          msgList.splice(msgIndex, 1)
        }
      }
      ElMessage.warning('会话已切换，图片未发送')
      return
    }
    
    // ✅ 通过WebSocket发送图片消息命令到Agent
    websocketManager.send({
      event: 'chat_send_image',
      direction: 'req',
      data: {
        client_id: chatStore.selectedClientId,
        seller_id: session.sellerId,
        item_id: session.itemId,
        image_url: imageUrl,
        image_width: imageWidth,
        image_height: imageHeight
      }
    })
    
    // ✅ 标记消息为发送成功（等待WebSocket收到真实消息后替换）
    const msgList = chatStore.messages[currentSessionId]
    if (msgList) {
      const msgIndex = msgList.findIndex(m => m.localId === optimisticMsg.localId)
      if (msgIndex !== -1) {
        msgList[msgIndex].status = 'success'
        msgList[msgIndex].pending = false
      }
    }
    
    await scrollToBottom()
  } catch (error) {
    console.error('[ChatPanel] 发送图片失败:', error)
    
    // ✅ 标记消息为发送失败（只在原会话中标记，如果会话已切换则不处理）
    const msgList = chatStore.messages[currentSessionId]
    if (msgList) {
      const msgIndex = msgList.findIndex(m => m.localId === optimisticMsg.localId)
      if (msgIndex !== -1) {
        msgList[msgIndex].status = 'failed'
        msgList[msgIndex].pending = false
        msgList[msgIndex].error = error.message || '发送失败'
      }
    }
    
    ElMessage.error('发送图片失败：' + (error.message || '未知错误'))
  }
}

const handleSend = async () => {
  if (!draftMessage.value.trim()) return
  const session = currentSession.value
  if (!session) {
    ElMessage.warning('请先选择会话')
    return
  }

  // ✅ 处理消息中的图片和表情标记
  let messageContent = draftMessage.value.trim()
  
  // 检查是否包含图片标记（base64格式，已废弃，现在直接上传）
  const imageRegex = /\[image:([^\]]+)\]/g
  const imageMatches = [...messageContent.matchAll(imageRegex)]
  
  if (imageMatches.length > 0) {
    // ✅ 如果包含图片标记，提示用户使用粘贴功能
    ElMessage.warning('请使用粘贴功能直接发送图片，或移除图片标记后发送文本消息')
    return
  }
  
  // 移除表情标记中的特殊格式，保留可读文本或转换为实际表情代码
  // 表情标记格式：[emoji:code] 或 [emoji:url]
  messageContent = messageContent.replace(/\[emoji:([^\]]+)\]/g, (match, code) => {
    // 查找对应的表情包
    const emoji = taobaoEmojis.value.find(e => e.code === code || e.url === code)
    if (emoji) {
      // 返回表情包的code，后端可以解析
      return emoji.code || code
    }
    return match // 如果找不到，保留原文本
  })
  
  draftMessage.value = ''

  // ✅ 生成临时ID和mid（前端生成，用于匹配）
  const tempId = generateTempId()
  const mid = generateMid()

  // ✅ 乐观更新：立即显示消息（不等待服务器响应）
  const optimisticMsg = {
    tempId: tempId,  // ✅ 前端生成的临时ID
    mid: mid,  // ✅ 用于匹配官方响应
    direction: 'send',
    message_type: 'text',
    message_content: messageContent,
    message_time: new Date().toISOString(),
    messageStatus: MessageStatus.PENDING,  // ✅ 使用状态机
    pending: true
  }
  console.log('[ChatPanel] 📤 发送消息，乐观更新:', {
    sessionId: session.sessionId,
    sellerId: session.sellerId,
    itemId: session.itemId,
    tempId,
    mid
  })
  chatStore.appendMessages(session.sessionId, [optimisticMsg])
  
  console.log('[ChatPanel] ✅ 发送消息（乐观更新）:', { tempId, mid, content: messageContent })
  
  // ✅ 设置超时检查 (30秒)
  setTimeout(() => {
    const msg = chatStore.findMessageByTempId(session.sessionId, tempId)
    if (msg && msg.pending) {
      console.warn('[ChatPanel] 消息发送超时:', tempId)
      chatStore.updateMessageByTempId(session.sessionId, tempId, {
        messageStatus: 3, // FAILED
        pending: false,
        error: '发送超时，请重试'
      })
    }
  }, 30000)
  
  // ✅ 更新会话的最后消息（立即更新，不等待服务器响应，不刷新整个列表）
  // ✅ 重要：传递sessionId，确保使用conversation_id（可能包含chat_id），而不是重新构建
  chatStore.addOrUpdateSession({
    sessionId: session.sessionId,  // ✅ 传递sessionId，确保使用conversation_id（可能包含chat_id）
    sellerId: session.sellerId,
    itemId: session.itemId,
    lastMessage: messageContent,
    lastTime: new Date().toISOString()
    // ✅ 不传递其他字段，保留已有的商品信息（itemTitle, itemImage, itemPrice等）
  })

  try {
    const res = await sendChatMessage({
      client_id: chatStore.selectedClientId,
      seller_id: session.sellerId,
      item_id: session.itemId,
      message: messageContent,
      temp_id: tempId,  // ✅ 传递临时ID
      mid: mid  // ✅ 传递mid
    })

    if (res.code === 200) {
      // ✅ API调用成功，等待WebSocket推送 message_created 和 message_confirmed 事件
      // 不需要手动更新状态，WebSocket会处理
      
      // ✅ 发送成功后滚动到底部
      await scrollToBottom()
      
      console.log('[ChatPanel] ✅ API调用成功，等待WebSocket事件:', { tempId, mid })
    } else {
      throw new Error(res.msg || '发送失败')
    }
  } catch (error) {
    console.error('[ChatPanel] ❌ 发送消息失败:', error)
    
    // ✅ 标记消息为发送失败
    chatStore.updateMessageByTempId(session.sessionId, tempId, {
      messageStatus: MessageStatus.FAILED,
      pending: false,
      error: error.message || '发送失败'
    })
    
    ElMessage.error('发送消息失败：' + (error.message || '未知错误'))
  }
}

const formatClientLabel = (client) => {
  if (!client) return ''
  const parts = []
  if (client.client_name) parts.push(client.client_name)
  if (client.hostname) parts.push(client.hostname)
  parts.push(`#${client.client_id.slice(-4)}`)
  return parts.join(' / ')
}

const formatRelativeTime = (time) => {
  if (!time) return ''
  return dayjs(time).fromNow()
}

const formatMessageTime = (time) => {
  if (!time) return ''
  const d = dayjs(time)
  if (!d.isValid()) return time
  return d.format('YYYY-MM-DD HH:mm')
}

// ✅ 格式化语音时长
const formatVoiceDuration = (msg) => {
  if (msg.voice_duration) {
    const duration = Math.ceil(msg.voice_duration)
    return `${duration}"`
  }
  return '0"'
}

// ✅ 格式化视频时长
const formatVideoDuration = (seconds) => {
  if (!seconds || seconds === 0) return '0:00'
  const mins = Math.floor(seconds / 60)
  const secs = Math.floor(seconds % 60)
  return `${mins}:${secs.toString().padStart(2, '0')}`
}

// ✅ 检查语音是否正在播放
const isVoicePlaying = (msg) => {
  return playingVoiceMessageId.value === (msg.messageId || msg.localId)
}

// ✅ 检测AMR格式（通过文件头 #!AMR）
const detectAMRFormat = (arrayBuffer) => {
  if (!arrayBuffer || arrayBuffer.byteLength < 6) {
    return false
  }
  
  // 读取前6个字节
  const view = new Uint8Array(arrayBuffer, 0, 6)
  // AMR格式文件头是 "#!AMR" (0x23 0x21 0x41 0x4D 0x52)
  // 或者 "#!AMR\n" (0x23 0x21 0x41 0x4D 0x52 0x0A)
  if (view[0] === 0x23 && view[1] === 0x21 && 
      view[2] === 0x41 && view[3] === 0x4D && view[4] === 0x52) {
    return true
  }
  
  return false
}

// ✅ 使用benz-amr-recorder播放AMR音频
const playAMRAudio = async (arrayBuffer, audioElement, messageId) => {
  return new Promise((resolve, reject) => {
    // ✅ 检查BenzAMRRecorder是否可用
    if (typeof window === 'undefined' || !window.BenzAMRRecorder) {
      console.error('[ChatPanel] ❌ BenzAMRRecorder未加载，请检查CDN或脚本')
      reject(new Error('AMR解码器未加载'))
      return
    }
    
    console.log('[ChatPanel] 🎵 开始播放AMR音频...')
    
    // ✅ 设置播放状态（必须在播放前设置，避免竞态条件）
    playingVoiceMessageId.value = messageId
    
    try {
      // ✅ 创建Blob URL（benz-amr-recorder需要URL）
      const blob = new Blob([arrayBuffer], { type: 'audio/amr' })
      const blobUrl = URL.createObjectURL(blob)
      
      // ✅ 创建BenzAMRRecorder实例
      const amr = new window.BenzAMRRecorder()
      
      // ✅ 保存AMR播放器实例，用于暂停/停止
      amrPlayerInstances.value.set(messageId, amr)
      
      // ✅ 检查API是否支持onEnded和onError（不同版本可能不同）
      let hasOnEnded = typeof amr.onEnded === 'function'
      let hasOnError = typeof amr.onError === 'function'
      let hasPause = typeof amr.pause === 'function'
      let hasStop = typeof amr.stop === 'function'
      
      console.log('[ChatPanel] BenzAMRRecorder API检查:', {
        hasOnEnded,
        hasOnError,
        hasPause,
        hasStop,
        methods: Object.keys(amr)
      })
      
      // ✅ 初始化AMR播放器
      amr.initWithUrl(blobUrl).then(() => {
        console.log('[ChatPanel] ✅ AMR初始化成功，开始播放')
        
        // ✅ 清理函数（在播放结束时调用）
        const cleanup = () => {
          // 清理Blob URL
          URL.revokeObjectURL(blobUrl)
          // 从Map中移除播放器实例
          amrPlayerInstances.value.delete(messageId)
          // 重置播放状态
          if (playingVoiceMessageId.value === messageId) {
            playingVoiceMessageId.value = null
          }
        }
        
        // ✅ 如果支持onEnded，注册播放结束回调
        if (hasOnEnded) {
          amr.onEnded(() => {
            console.log('[ChatPanel] ✅ AMR播放结束')
            cleanup()
            resolve()
          })
        } else {
          // ✅ 如果不支持onEnded，使用其他方式监听（如定时检查或监听播放状态）
          console.warn('[ChatPanel] ⚠️ onEnded方法不可用，将使用替代方案')
          // 创建一个定时器来检查播放状态
          const checkInterval = setInterval(() => {
            // 检查是否还在播放（如果amr有isPlaying方法）
            if (typeof amr.isPlaying === 'function' && !amr.isPlaying()) {
              clearInterval(checkInterval)
              console.log('[ChatPanel] ✅ AMR播放结束（通过定时检查）')
              cleanup()
              resolve()
            }
            // ✅ 检查播放状态是否被外部改变（用户点击了停止）
            if (playingVoiceMessageId.value !== messageId) {
              clearInterval(checkInterval)
              console.log('[ChatPanel] ✅ AMR播放被停止')
              cleanup()
              resolve()
            }
          }, 100) // 每100ms检查一次
          
          // 设置超时（最长30秒，防止内存泄漏）
          setTimeout(() => {
            clearInterval(checkInterval)
            if (playingVoiceMessageId.value === messageId) {
              console.log('[ChatPanel] ⚠️ AMR播放超时，假设已结束')
              cleanup()
              resolve()
            }
          }, 30000)
        }
        
        // ✅ 如果支持onError，注册错误回调
        if (hasOnError) {
          amr.onError((err) => {
            console.error('[ChatPanel] ❌ AMR播放错误:', err)
            // 清理
            URL.revokeObjectURL(blobUrl)
            amrPlayerInstances.value.delete(messageId)
            playingVoiceMessageId.value = null
            reject(err)
          })
        }
        
        // ✅ 开始播放
        try {
          amr.play()
          console.log('[ChatPanel] ✅ AMR播放开始')
        } catch (playErr) {
          console.error('[ChatPanel] ❌ AMR播放失败:', playErr)
          URL.revokeObjectURL(blobUrl)
          amrPlayerInstances.value.delete(messageId)
          playingVoiceMessageId.value = null
          reject(playErr)
        }
        
      }).catch((err) => {
        console.error('[ChatPanel] ❌ AMR初始化失败:', err)
        // 清理
        URL.revokeObjectURL(blobUrl)
        amrPlayerInstances.value.delete(messageId)
        playingVoiceMessageId.value = null
        reject(new Error('AMR初始化失败: ' + (err.message || err)))
      })
      
    } catch (err) {
      console.error('[ChatPanel] ❌ AMR播放器创建失败:', err)
      playingVoiceMessageId.value = null
      reject(err)
    }
  })
}

// ✅ 处理语音播放
const handleVoicePlay = async (msg) => {
  const messageId = msg.messageId || msg.localId
  
  // ✅ 如果正在播放当前消息，则暂停
  if (playingVoiceMessageId.value === messageId) {
    // ✅ 检查是否是AMR播放器
    const amrPlayer = amrPlayerInstances.value.get(messageId)
    if (amrPlayer) {
      // ✅ 尝试停止AMR播放器
      console.log('[ChatPanel] 🛑 停止AMR播放')
      try {
        // 尝试调用stop方法（如果存在）
        if (typeof amrPlayer.stop === 'function') {
          amrPlayer.stop()
        } else if (typeof amrPlayer.pause === 'function') {
          amrPlayer.pause()
        }
        // 清理播放器实例
        amrPlayerInstances.value.delete(messageId)
      } catch (err) {
        console.warn('[ChatPanel] ⚠️ 停止AMR播放器失败:', err)
      }
    } else {
      // ✅ 如果不是AMR，尝试暂停audio元素
      const audioElement = document.querySelector(`audio[data-message-id="${messageId}"]`)
      if (audioElement) {
        audioElement.pause()
      }
    }
    playingVoiceMessageId.value = null
    return
  }
  
  // ✅ 暂停其他正在播放的语音
  if (playingVoiceMessageId.value) {
    const currentMessageId = playingVoiceMessageId.value
    
    // ✅ 检查是否是AMR播放器
    const amrPlayer = amrPlayerInstances.value.get(currentMessageId)
    if (amrPlayer) {
      // ✅ 尝试停止AMR播放器
      console.log('[ChatPanel] 🛑 停止其他AMR播放')
      try {
        if (typeof amrPlayer.stop === 'function') {
          amrPlayer.stop()
        } else if (typeof amrPlayer.pause === 'function') {
          amrPlayer.pause()
        }
        amrPlayerInstances.value.delete(currentMessageId)
      } catch (err) {
        console.warn('[ChatPanel] ⚠️ 停止其他AMR播放器失败:', err)
      }
    } else {
      // ✅ 如果不是AMR，尝试暂停audio元素
      const currentAudio = document.querySelector(`audio[data-message-id="${currentMessageId}"]`)
      if (currentAudio) {
        currentAudio.pause()
        currentAudio.currentTime = 0
      }
    }
  }
  
  // ✅ 检查消息是否有voice_url
  if (!msg.voice_url) {
    console.error('[ChatPanel] ❌ 语音消息缺少voice_url:', msg)
    ElMessage.error('语音消息缺少播放地址')
    return
  }
  
  // ✅ 先检测是否是AMR格式（通过fetch获取文件头）
  // 如果是AMR格式，直接使用AMR播放器，不通过audio元素
  const checkAndPlayAMR = async () => {
    try {
      // ✅ 使用HEAD请求或只获取前几个字节来检测文件类型（避免下载整个文件）
      const response = await fetch(msg.voice_url, {
        method: 'GET',
        mode: 'cors',
        credentials: 'omit',
        headers: {
          'Range': 'bytes=0-5' // 只获取前6个字节用于检测AMR文件头
        }
      })
      
      if (response.ok || response.status === 206) {
        const buffer = await response.arrayBuffer()
        const isAMR = detectAMRFormat(buffer)
        
        if (isAMR) {
          console.log('[ChatPanel] 🎵 检测到AMR格式，直接使用AMR播放器')
          // ✅ 获取完整音频数据用于播放
          const fullResponse = await fetch(msg.voice_url, {
            method: 'GET',
            mode: 'cors',
            credentials: 'omit'
          })
          
          if (fullResponse.ok || fullResponse.status === 206) {
            const fullBuffer = await fullResponse.arrayBuffer()
            const audioElement = document.querySelector(`audio[data-message-id="${messageId}"]`)
            await playAMRAudio(fullBuffer, audioElement, messageId)
            return true // 成功播放，不再执行后续逻辑
          }
        }
      }
    } catch (err) {
      console.warn('[ChatPanel] ⚠️ AMR格式检测失败，将使用普通播放方式:', err)
    }
    return false // 不是AMR或检测失败，继续使用普通播放方式
  }
  
  // ✅ 先尝试AMR播放（如果是AMR格式）
  const isAMRPlayed = await checkAndPlayAMR()
  if (isAMRPlayed) {
    return // AMR播放成功，退出
  }
  
  // ✅ 如果不是AMR格式，使用普通audio元素播放
  // 通过DOM查找audio元素并播放（因为refs可能还没更新）
  nextTick(async () => {
    const audioElement = document.querySelector(`audio[data-message-id="${messageId}"]`)
    
    if (!audioElement) {
      console.warn('[ChatPanel] 未找到语音元素:', messageId)
      return
    }
    
    // ✅ 检查audio元素的src是否设置（直接使用audio的src，不再使用source元素）
    const currentSrc = audioElement.src
    if (!currentSrc || currentSrc !== msg.voice_url) {
      // ✅ 如果src没有设置或与消息的voice_url不一致，更新它
      console.log('[ChatPanel] 更新audio元素src:', { currentSrc, voice_url: msg.voice_url })
      audioElement.src = msg.voice_url
      // 强制重新加载
      audioElement.load()
    }
    
    // ✅ 设置播放状态（必须在播放前设置，避免竞态条件）
    playingVoiceMessageId.value = messageId
    
    // ✅ 对于206 Partial Content响应，浏览器可能不会立即触发loadedmetadata事件
    // 但只要有部分数据（readyState >= 1），就可以开始播放
    // 所以我们可以直接尝试播放，不等待预加载完成
    
    if (audioElement.readyState === 0) {
      // 元素还没有加载，先尝试加载
      audioElement.load()
      
      // ✅ 等待很短时间（2秒），如果readyState >= 1就立即播放
      // 对于206 Partial Content，通常很快就能开始播放
      try {
        await Promise.race([
          new Promise((resolve) => {
            // 如果已经加载完成，直接resolve
            if (audioElement.readyState >= 1) {
              resolve()
              return
            }
            
            let resolved = false
            const resolveOnce = () => {
              if (!resolved) {
                resolved = true
                resolve()
              }
            }
            
            // ✅ 定期检查readyState，如果已经加载了部分数据（readyState >= 1），立即resolve
            // 这对于206 Partial Content响应特别重要
            const checkInterval = setInterval(() => {
              if (audioElement.readyState >= 1) {
                clearInterval(checkInterval)
                resolveOnce()
              }
            }, 100) // 每100ms检查一次
            
            // ✅ 优先监听canplay事件（表示可以开始播放，比loadedmetadata更快）
            audioElement.addEventListener('canplay', () => {
              clearInterval(checkInterval)
              resolveOnce()
            }, { once: true })
            
            audioElement.addEventListener('loadedmetadata', () => {
              clearInterval(checkInterval)
              resolveOnce()
            }, { once: true })
          }),
          new Promise((resolve) => {
            // ✅ 2秒后，如果readyState >= 1就继续，否则也继续（让浏览器尝试播放）
            setTimeout(() => {
              if (audioElement.readyState >= 1) {
                console.log('[ChatPanel] 2秒后readyState >= 1，继续播放:', audioElement.readyState)
              } else {
                console.log('[ChatPanel] 2秒后readyState仍为0，将尝试直接播放（浏览器会自动加载）')
              }
              resolve() // 总是resolve，让播放逻辑继续
            }, 2000) // ✅ 只等待2秒，因为206 Partial Content应该很快就能开始播放
          })
        ])
        console.log('[ChatPanel] ✅ 语音准备就绪:', messageId, 'readyState:', audioElement.readyState, 'duration:', audioElement.duration || '未知')
      } catch (err) {
        // 不应该到达这里，因为超时Promise总是resolve
        console.warn('[ChatPanel] 语音加载异常:', err.message)
      }
    } else {
      // ✅ 如果已经加载了部分数据（readyState >= 1），可以直接播放
      console.log('[ChatPanel] 语音已加载，直接播放:', messageId, 'readyState:', audioElement.readyState, 'duration:', audioElement.duration || '未知')
    }
    
    // ✅ 检查是否仍然是当前播放的消息（避免竞态条件）
    if (playingVoiceMessageId.value !== messageId) {
      console.log('[ChatPanel] 播放已取消（用户切换了消息）:', messageId)
      return
    }
    
      // ✅ 播放选中的语音（使用try-catch处理可能的播放错误）
      try {
        // ✅ 检查audio元素是否还有错误
        if (audioElement.error) {
          console.error('[ChatPanel] audio元素有错误，无法播放:', {
            errorCode: audioElement.error.code,
            readyState: audioElement.readyState,
            networkState: audioElement.networkState
          })
          // 触发错误处理
          handleVoiceError({ target: audioElement })
          return
        }
        
        const playPromise = audioElement.play()
        
        // ✅ 处理play()返回的Promise
        if (playPromise !== undefined) {
          await playPromise
          console.log('[ChatPanel] ✅ 语音播放成功:', messageId, 'duration:', audioElement.duration)
        }
      } catch (err) {
        // ✅ 忽略"play()被pause()中断"的错误（这是正常的，当用户快速切换时）
        if (err.name === 'AbortError' || err.message.includes('interrupted by a call to pause')) {
          console.log('[ChatPanel] 播放被中断（用户切换了消息）:', messageId)
          return
        }
        
        console.error('[ChatPanel] 播放语音失败:', {
          error: err.message,
          name: err.name,
          readyState: audioElement.readyState,
          networkState: audioElement.networkState,
          errorCode: audioElement.error?.code,
          voice_url: msg.voice_url
        })
        
        // ✅ 如果播放失败，检查是否是加载问题
        if (audioElement.error) {
          handleVoiceError({ target: audioElement })
        } else {
          ElMessage.error('语音播放失败: ' + (err.message || '未知错误'))
          playingVoiceMessageId.value = null
        }
      }
  })
}

// ✅ 处理语音加载完成
const handleVoiceLoaded = (event) => {
  const audioElement = event.target
  const messageId = audioElement.dataset.messageId
  if (messageId && audioElement.duration) {
    console.debug('[ChatPanel] 语音加载完成:', messageId, '时长:', audioElement.duration)
  }
}

// ✅ 处理语音播放时间更新
const handleVoiceTimeUpdate = (event) => {
  const audioElement = event.target
  const messageId = audioElement.dataset.messageId
  if (messageId) {
    voiceCurrentTime.value[messageId] = audioElement.currentTime
  }
}

// ✅ 处理语音播放结束
const handleVoiceEnded = (event) => {
  const audioElement = event.target
  const messageId = audioElement.dataset.messageId
  if (messageId === playingVoiceMessageId.value) {
    playingVoiceMessageId.value = null
    audioElement.currentTime = 0
  }
}

// ✅ 处理语音加载错误
const handleVoiceError = async (event) => {
  const audioElement = event.target
  const messageId = audioElement.dataset.messageId
  const error = audioElement.error
  
  // ✅ 获取错误详情
  let errorMessage = '未知错误'
  if (error) {
    switch (error.code) {
      case MediaError.MEDIA_ERR_ABORTED:
        errorMessage = '播放被中止'
        break
      case MediaError.MEDIA_ERR_NETWORK:
        errorMessage = '网络错误'
        break
      case MediaError.MEDIA_ERR_DECODE:
        errorMessage = '解码错误'
        break
      case MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED:
        errorMessage = '不支持的格式'
        break
      default:
        errorMessage = `错误代码: ${error.code}`
    }
  }
  
  // ✅ 直接使用audio元素的src（不再使用source元素）
  const voiceUrl = audioElement.src || ''
  
  console.warn('[ChatPanel] 语音加载失败:', {
    messageId,
    voiceUrl,
    errorCode: error?.code,
    errorMessage,
    readyState: audioElement.readyState,
    networkState: audioElement.networkState
  })
  
  // ✅ 检查是否是Blob URL的错误（如果是，说明fallback已经尝试过了，直接报错）
  const isBlobUrl = audioElement.dataset.isBlobUrl === 'true'
  if (isBlobUrl) {
    console.error('[ChatPanel] ❌ Blob URL也加载失败，说明音频数据有问题:', {
      messageId,
      blobUrl: voiceUrl,
      errorCode: error?.code,
      errorMessage
    })
    ElMessage.error(`语音加载失败: ${errorMessage}（Blob URL也无法播放）`)
    playingVoiceMessageId.value = null
    return
  }
  
  // ✅ 如果audio元素加载失败，尝试fallback方案
  if (voiceUrl && !audioElement.dataset.fallbackTried) {
    try {
      audioElement.dataset.fallbackTried = 'true' // 标记已尝试fallback
      
      console.log('[ChatPanel] 原始URL加载失败，尝试fallback方案:', voiceUrl)
      
      // ✅ 方案1（推荐）：直接用原始URL重新加载（浏览器会自动处理Range请求）
      // 如果之前失败是临时网络问题，重新加载可能成功
      console.log('[ChatPanel] 🔄 尝试方案1：直接用原始URL重新加载')
      
      // 清除旧的错误监听器，避免循环
      audioElement.removeEventListener('error', handleVoiceError)
      
      // 直接使用原始URL
      audioElement.src = voiceUrl
      audioElement.load()
      
      // 等待一小段时间，看是否加载成功
      await new Promise((resolve) => setTimeout(resolve, 500))
      
      // 检查是否还有错误
      if (!audioElement.error && audioElement.readyState >= 2) {
        console.log('[ChatPanel] ✅ 方案1成功：原始URL重新加载成功')
        // 重新添加错误监听器
        audioElement.addEventListener('error', handleVoiceError, { once: true })
        return
      }
      
      // ✅ 方案2：如果方案1失败，尝试使用fetch + Range请求完整文件
      console.log('[ChatPanel] 🔄 方案1失败，尝试方案2：fetch + Range请求完整文件')
      
      let response = null
      
      // 尝试cors模式，使用Range请求完整文件
      try {
        response = await fetch(voiceUrl, {
          method: 'GET',
          mode: 'cors',
          credentials: 'omit',
          headers: {
            'Accept': 'audio/*',
            'Range': 'bytes=0-' // ✅ 请求完整文件（OSS会忽略Range=0-，直接返回全量）
          }
        })
      } catch (corsError) {
        console.warn('[ChatPanel] CORS模式失败:', corsError)
        throw new Error('CORS限制：无法读取音频数据')
      }
      
      // ✅ 检查响应状态（206或200都可以）
      const status = response.status
      const isPartialContent = status === 206
      const isSuccess = status === 200 || isPartialContent
      
      if (!isSuccess) {
        throw new Error(`HTTP ${status}: ${response.statusText || '请求失败'}`)
      }
      
      // ✅ 诊断：检查是否是206 Partial Content
      const contentRange = response.headers.get('Content-Range')
      const contentLength = response.headers.get('Content-Length')
      const rawContentType = response.headers.get('Content-Type')
      
      console.log('[ChatPanel] 📊 fetch响应信息:', {
        status,
        isPartialContent,
        contentRange,
        contentLength,
        rawContentType: rawContentType || '(未设置)'
      })
      
      // ✅ 检查Content-Type，如果是application/octet-stream或空，说明服务器没有正确设置类型
      // 咸鱼语音通常是AAC或AMR格式，需要明确指定
      let contentType = rawContentType
      if (!contentType || contentType === 'application/octet-stream' || contentType === 'binary/octet-stream') {
        console.warn('[ChatPanel] ⚠️ Content-Type无效或未设置，将尝试常见音频格式')
        // 默认尝试AAC（咸鱼语音通常是AAC格式）
        contentType = 'audio/aac'
      }
      
      // ✅ 确保是有效的音频MIME类型
      if (!contentType.startsWith('audio/')) {
        console.warn('[ChatPanel] ⚠️ Content-Type不是音频类型，将尝试AAC格式')
        contentType = 'audio/aac'
      }
      
      if (contentRange) {
        // 解析Content-Range: bytes 0-1048575/4343285
        const match = contentRange.match(/bytes (\d+)-(\d+)\/(\d+)/)
        if (match) {
          const [, start, end, total] = match
          const receivedSize = parseInt(end) - parseInt(start) + 1
          const totalSize = parseInt(total)
          console.warn('[ChatPanel] ⚠️ 收到206 Partial Content:', {
            received: receivedSize,
            total: totalSize,
            percent: ((receivedSize / totalSize) * 100).toFixed(2) + '%'
          })
          
          // 如果只收到部分数据，使用arrayBuffer明确类型
          if (receivedSize < totalSize) {
            console.warn('[ChatPanel] ⚠️ 只收到部分数据，尝试请求完整文件')
            // 重新请求完整文件
            const fullResponse = await fetch(voiceUrl, {
              method: 'GET',
              mode: 'cors',
              credentials: 'omit',
              headers: {
                'Accept': 'audio/*'
                // 不设置Range，让服务器返回完整文件
              }
            })
            
            if (fullResponse.ok || fullResponse.status === 206) {
              response = fullResponse
              console.log('[ChatPanel] ✅ 重新请求后状态:', fullResponse.status)
            }
          }
        }
      }
      
      // ✅ 方案3：使用arrayBuffer + Blob明确类型（避免206导致的问题）
      const buffer = await response.arrayBuffer()
      
      if (!buffer || buffer.byteLength === 0) {
        throw new Error('音频数据为空')
      }
      
      console.log('[ChatPanel] ✅ fetch成功，ArrayBuffer大小:', buffer.byteLength, '字节')
      
      // ✅ 检测是否是AMR格式（通过文件头 #!AMR）
      const isAMR = detectAMRFormat(buffer)
      
      if (isAMR) {
        console.log('[ChatPanel] 🎵 检测到AMR格式，使用amr.js解码播放')
        // ✅ 使用amr.js解码并播放AMR音频
        await playAMRAudio(buffer, audioElement, messageId)
        return // AMR播放成功，退出
      }
      
      // ✅ 非AMR格式，尝试多种音频格式，找到能播放的
      const audioTypes = [
        contentType, // 首先尝试从响应头获取的类型
        'audio/aac', // AAC格式（咸鱼语音常用）
        'audio/mpeg', // MP3格式（备选）
        'audio/mp4', // MP4音频
        'audio/ogg' // OGG格式（备选）
      ]
      
      // 移除重复的类型
      const uniqueTypes = [...new Set(audioTypes.filter(Boolean))]
      
      console.log('[ChatPanel] 🔍 将尝试以下音频格式:', uniqueTypes)
      
      // ✅ 创建Blob，优先使用从响应头获取的类型
      let audioBlob = new Blob([buffer], { type: contentType })
      
      // ✅ 创建Blob URL并尝试播放
      // 如果第一个类型失败，会通过错误处理尝试其他类型
      const blobUrl = URL.createObjectURL(audioBlob)
      
      // ✅ 标记这是Blob URL（用于区分原始URL和Blob URL的错误）
      audioElement.dataset.isBlobUrl = 'true'
      audioElement.dataset.triedTypes = contentType // 记录已尝试的类型
      audioElement.dataset.allTypes = uniqueTypes.join(',') // 记录所有可尝试的类型
      audioElement.dataset.typeIndex = '0' // 当前尝试的类型索引
      
      // 清除旧的错误监听器
      audioElement.removeEventListener('error', handleVoiceError)
      
      // 使用Blob URL
      audioElement.src = blobUrl
      audioElement.load()
      
      // ✅ 等待一小段时间，检查是否加载成功
      await new Promise((resolve) => setTimeout(resolve, 300))
      
      // ✅ 检查是否加载成功（如果readyState >= 2说明可以播放）
      if (!audioElement.error && audioElement.readyState >= 2) {
        console.log('[ChatPanel] ✅ 使用Blob URL成功加载语音:', messageId, '类型:', contentType)
        // 重新添加错误监听器
        audioElement.addEventListener('error', handleVoiceError, { once: true })
        
        // 如果当前正在播放这个语音，继续播放
        if (playingVoiceMessageId.value === messageId) {
          try {
            const playPromise = audioElement.play()
            if (playPromise !== undefined) {
              await playPromise
              console.log('[ChatPanel] ✅ Blob URL播放成功:', messageId)
            }
          } catch (playErr) {
            if (playErr.name === 'AbortError' || playErr.message.includes('interrupted by a call to pause')) {
              console.log('[ChatPanel] Blob URL播放被中断:', messageId)
              return
            }
            console.error('[ChatPanel] Blob URL播放失败:', playErr)
            ElMessage.error('语音播放失败: ' + playErr.message)
            playingVoiceMessageId.value = null
          }
        }
        
        // ✅ 清理：在播放结束后释放Blob URL
        audioElement.addEventListener('ended', () => {
          URL.revokeObjectURL(blobUrl)
          audioElement.dataset.isBlobUrl = ''
        }, { once: true })
        
        return // 成功加载，退出
      }
      
      // ✅ 如果第一个类型失败，尝试其他类型
      console.warn('[ChatPanel] ⚠️ 类型', contentType, '加载失败，尝试其他类型')
      
      // 找到当前类型在列表中的索引
      let currentIndex = uniqueTypes.indexOf(contentType)
      if (currentIndex === -1) currentIndex = 0
      
      // 尝试下一个类型
      for (let i = currentIndex + 1; i < uniqueTypes.length; i++) {
        const nextType = uniqueTypes[i]
        console.log(`[ChatPanel] 🔄 尝试类型 ${i + 1}/${uniqueTypes.length}:`, nextType)
        
        // 释放旧的Blob URL
        URL.revokeObjectURL(blobUrl)
        
        // 创建新的Blob，使用新类型
        const newBlob = new Blob([buffer], { type: nextType })
        const newBlobUrl = URL.createObjectURL(newBlob)
        
        // 更新audio元素
        audioElement.dataset.triedTypes = nextType
        audioElement.dataset.typeIndex = i.toString()
        audioElement.src = newBlobUrl
        audioElement.load()
        
        // 等待加载
        await new Promise((resolve) => setTimeout(resolve, 300))
        
        // 检查是否成功
        if (!audioElement.error && audioElement.readyState >= 2) {
          console.log('[ChatPanel] ✅ 类型', nextType, '加载成功！')
          // 重新添加错误监听器
          audioElement.addEventListener('error', handleVoiceError, { once: true })
          
          // 如果当前正在播放这个语音，继续播放
          if (playingVoiceMessageId.value === messageId) {
            try {
              const playPromise = audioElement.play()
              if (playPromise !== undefined) {
                await playPromise
                console.log('[ChatPanel] ✅ 播放成功，使用的类型:', nextType)
              }
            } catch (playErr) {
              if (playErr.name === 'AbortError' || playErr.message.includes('interrupted by a call to pause')) {
                console.log('[ChatPanel] 播放被中断:', messageId)
                return
              }
              console.error('[ChatPanel] 播放失败:', playErr)
            }
          }
          
          // ✅ 清理：在播放结束后释放Blob URL
          audioElement.addEventListener('ended', () => {
            URL.revokeObjectURL(newBlobUrl)
            audioElement.dataset.isBlobUrl = ''
          }, { once: true })
          
          return // 成功加载，退出
        } else {
          console.warn('[ChatPanel] ⚠️ 类型', nextType, '也失败，错误码:', audioElement.error?.code)
          // 继续尝试下一个类型
        }
      }
      
      // ✅ 如果所有类型都失败，抛出错误
      throw new Error(`所有音频格式都无法播放: ${uniqueTypes.join(', ')}`)
      
    } catch (error) {
      console.error('[ChatPanel] ❌ 所有fallback方案都失败:', {
        error: error.message,
        stack: error.stack,
        voiceUrl,
        messageId
      })
      
      // ✅ 根据错误类型给出不同的提示
      let errorMsg = '语音加载失败'
      if (error.message.includes('CORS')) {
        errorMsg = '语音加载失败：跨域限制（CORS），请检查OSS配置'
      } else if (error.message.includes('网络') || error.message.includes('fetch')) {
        errorMsg = '语音加载失败：网络错误，请检查网络连接'
      } else if (error.message.includes('HTTP')) {
        errorMsg = '语音加载失败：服务器错误'
      }
      
      ElMessage.error(errorMsg)
      playingVoiceMessageId.value = null
    }
  } else {
    console.error('[ChatPanel] ❌ 语音加载失败，且已尝试fallback或URL为空:', {
      messageId,
      voiceUrl,
      fallbackTried: audioElement.dataset.fallbackTried,
      errorCode: error?.code,
      errorMessage
    })
    
    if (!audioElement.dataset.fallbackTried && voiceUrl) {
      ElMessage.error(`语音加载失败: ${errorMessage}`)
    } else if (!voiceUrl) {
      ElMessage.error('语音URL为空，无法播放')
    }
    playingVoiceMessageId.value = null
  }
}

// ✅ 会话搜索处理（当前使用computed自动更新，预留扩展空间）
const handleSessionSearch = () => {
  // 当前使用filteredSessions计算属性自动过滤
  // 可在此添加防抖、日志等功能
}

// ✅ 会话列表分页状态
const sessionPageNum = ref(1) // 兼容旧逻辑，虽然这里主要用cursor
const nextCursor = ref(null)  // ✅ 游标分页
const hasMoreSessions = ref(true)
const loadingMoreSessions = ref(false)
const sessionScrollRef = ref(null)

const refreshSessions = async (forceRefresh = false, loadMore = false) => {
  if (!chatStore.selectedClientId) {
    ElMessage.warning('请先选择客户端')
    return
  }

  // ✅ 如果是加载更多，但正在加载或没有更多数据，直接返回
  if (loadMore && (loadingMoreSessions.value || !hasMoreSessions.value)) {
    return
  }

  // ✅ 如果是强制刷新或普通刷新（非加载更多），重置分页状态
  if (!loadMore) {
    sessionPageNum.value = 1
    nextCursor.value = null // 重置游标
    hasMoreSessions.value = true
  }

  // ✅ 优先使用缓存（除非强制刷新或加载更多）
  if (!forceRefresh && !loadMore) {
    const cachedSessions = getCachedSessions(chatStore.selectedClientId)
    if (cachedSessions && cachedSessions.length > 0) {
      chatStore.setSessions(cachedSessions)
      return
    }
  }

  // 设置加载状态
  if (loadMore) {
    loadingMoreSessions.value = true
  } else {
    sessionsLoading.value = true
  }

  try {
    const limit = 20 // 后端默认limit=20
    // ✅ 使用游标分页参数
    const params = {
      client_id: chatStore.selectedClientId,
      limit: limit,
      cursor: loadMore ? nextCursor.value : null,
      force_refresh: forceRefresh
    }
    
    const res = await getChatSessions(params)

    if (res.code === 200 && res.data && res.data.sessions) {
      // 更新会话列表
      const mappedSessions = res.data.sessions
        .filter(s => {
          // ✅ 双重检查：确保只显示当前选中客户端的会话
          if (s.client_id !== chatStore.selectedClientId) {
            return false
          }
          return true
        })
        .map(s => ({
          sessionId: s.conversation_id,
          sellerId: s.seller_id,
          sellerName: s.seller_name,
          itemId: s.item_id,
          itemTitle: s.item_title,
          itemPrice: s.item_price || null,
          itemImage: s.item_image || s.itemImage || null,
          itemTags: s.item_tags || s.itemTags || [],
          itemStatus: s.item_status !== undefined ? s.item_status : null,
          itemStatusStr: s.item_status_str !== undefined ? s.item_status_str : null,
          itemDetail: (s.image_infos || s.video_play_info) ? {
            imageInfos: s.image_infos || [],
            videoPlayInfo: s.video_play_info || null
          } : null,
          isBlocked: s.is_blocked || false,
          clientId: s.client_id,
          clientName: s.client_name,
          lastMessage: s.last_message,
          lastTime: s.last_message_time,
          avatar: s.seller_avatar || s.avatar || '',
          unread: 0
        }))
      
      // ✅ 判断是否有更多数据
      // 优先使用后端返回的 has_more 字段，如果不存在则使用数量判断
      if (res.data.has_more !== undefined) {
        hasMoreSessions.value = res.data.has_more
        // ✅ 更新游标
        if (res.data.next_cursor) {
          nextCursor.value = res.data.next_cursor
        }
      } else {
        // 如果返回数量小于请求数量，说明没有更多了
        hasMoreSessions.value = mappedSessions.length >= pageSize
      }

      if (loadMore) {
        // ✅ 加载更多模式：追加数据
        // 过滤掉已存在的会话（避免重复）
        const existingSessionIds = new Set(chatStore.sessions.map(s => s.sessionId))
        const newUniqueSessions = mappedSessions.filter(s => !existingSessionIds.has(s.sessionId))
        
        if (newUniqueSessions.length > 0) {
          const allSessions = [...chatStore.sessions, ...newUniqueSessions]
          // 重新排序
          allSessions.sort((a, b) => {
            const timeA = new Date(a.lastTime).getTime()
            const timeB = new Date(b.lastTime).getTime()
            return timeB - timeA // 降序排列
          })
          chatStore.setSessions(allSessions)
          // 不需要 sessionPageNum++，因为使用的是游标
        } else {
          // 如果没有新数据，可能是去重后为空，或者是数据真的完了
          if (!res.data.has_more) {
            hasMoreSessions.value = false
          }
        }
      } else {
        // ✅ 刷新模式：执行原本的智能合并逻辑
        const existingSessions = chatStore.sessions || []
        const existingSessionsMap = new Map(
          existingSessions
            .filter(s => s.clientId === chatStore.selectedClientId)
            .map(s => [s.sessionId, s])
        )
        
        const existingSessionIds = new Set(mappedSessions.map(s => s.sessionId))
        
        // 找出本地有但服务器没有的（可能是新创建的临时会话）
        const localOnlySessions = existingSessions.filter(s => {
          return s.clientId === chatStore.selectedClientId && 
                 !existingSessionIds.has(s.sessionId) &&
                 // 确保不是旧数据残留
                 (Date.now() - new Date(s.lastTime).getTime() < 24 * 60 * 60 * 1000)
        })
        
        // 合并服务器数据和本地数据
        const mergedSessions = [
          ...mappedSessions.map(serverSession => {
            const localSession = existingSessionsMap.get(serverSession.sessionId)
            if (localSession) {
              // 保留本地更详细的商品信息
              if (!serverSession.itemDetail && localSession.itemDetail) {
                serverSession.itemDetail = localSession.itemDetail
              }
              // 保留本地未读数（如果服务器没返回）
              serverSession.unread = localSession.unread || 0
            }
            return serverSession
          }),
          ...localOnlySessions
        ]
        
        // 按时间降序排序
        mergedSessions.sort((a, b) => {
          const timeA = new Date(a.lastTime).getTime()
          const timeB = new Date(b.lastTime).getTime()
          return timeB - timeA
        })
        
        chatStore.setSessions(mergedSessions)
        // 不需要处理 sessionPageNum
      }
      
      // 更新缓存
      setCachedSessions(chatStore.selectedClientId, chatStore.sessions)
    } else {
      if (loadMore) {
        hasMoreSessions.value = false
      } else {
        chatStore.setSessions([])
      }
    }
  } catch (error) {
    console.error('[ChatPanel] 获取会话列表失败:', error)
    ElMessage.error('获取会话列表失败')
  } finally {
    sessionsLoading.value = false
    loadingMoreSessions.value = false
  }
}

// ✅ 处理会话列表滚动穿透
const handleSessionWheel = (e) => {
  const wrap = sessionScrollRef.value?.wrapRef
  if (!wrap) return
  
  const { scrollTop, scrollHeight, clientHeight } = wrap
  const isAtTop = scrollTop === 0
  const isAtBottom = Math.abs(scrollHeight - clientHeight - scrollTop) < 1
  
  if ((isAtTop && e.deltaY < 0) || (isAtBottom && e.deltaY > 0)) {
    e.preventDefault()
    e.stopPropagation()
  }
}

// ✅ 处理会话列表滚动（加载更多）
const handleSessionScroll = ({ scrollTop }) => {
  const wrap = sessionScrollRef.value?.wrapRef
  if (!wrap) return
  
  const { scrollHeight, clientHeight } = wrap
  // 滚动到底部前50px时触发加载更多
  if (scrollHeight - scrollTop - clientHeight < 50) {
    if (!loadingMoreSessions.value && hasMoreSessions.value) {
      refreshSessions(false, true) // false=不强制刷新, true=加载更多
    }
  }
}

// 监听客户端切换，自动加载会话列表
watch(() => chatStore.selectedClientId, (newId, oldId) => {
  // 避免初始化时重复加载
  if (newId && newId !== oldId) {
    refreshSessions()
  } else if (!newId) {
    chatStore.setSessions([])
  }
})

// 监听面板可见性，当面板打开时自动刷新会话列表
watch(() => chatStore.visible, async (visible) => {
  if (visible) {
    // ✅ 面板打开时，如果agentStore客户端列表为空，先加载客户端列表
    if (!agentStore.clients || agentStore.clients.length === 0) {
      console.log('[ChatPanel] 客户端列表为空，正在加载...')
      try {
        const res = await getAgentClientList({ page_num: 1, page_size: 1000 })
        if (res.code === 200 && res.data && res.data.rows) {
          agentStore.setClients(res.data.rows.map(client => ({
            ...client,
            client_type: client.client_type || '',
            online_status: client.online_status || 'offline',
            login_status: client.login_status || 'not_logged_in'
          })))
          console.log('[ChatPanel] ✅ 已加载客户端列表:', res.data.rows.length, '个客户端')
        }
      } catch (error) {
        console.error('[ChatPanel] 加载客户端列表失败:', error)
        ElMessage.warning('加载客户端列表失败，请稍后重试')
      }
    }
    
    // 面板打开时：
    // ✅ 只有在完全没有选中客户端时才自动选择（避免覆盖用户的手动选择）
    if (!chatStore.selectedClientId && availableClients.value.length > 0) {
      console.log('[ChatPanel] 自动选择第一个可用客户端:', availableClients.value[0].client_id)
      chatStore.selectClient(availableClients.value[0].client_id)
      // 选择后立即刷新会话列表
      refreshSessions()
    } else if (chatStore.selectedClientId) {
      // 如果已经有选中的客户端，直接刷新会话列表
      refreshSessions()
    }
  }
})

// 组件挂载时，如果已经选择了客户端，则加载会话列表
onMounted(async () => {
  if (chatStore.selectedClientId) {
    // ✅ 加载表情包（只有选择了客户端才加载）
    await loadTaobaoEmojis()
    
    await refreshSessions()
    // ✅ 如果刷新会话列表后，有当前选中的会话，自动加载该会话的消息
    if (chatStore.currentSessionId && chatStore.sessions.length > 0) {
      const currentSession = chatStore.sessions.find(s => s.sessionId === chatStore.currentSessionId)
      if (currentSession) {
        console.log('[ChatPanel] 页面刷新后自动加载当前会话消息:', chatStore.currentSessionId)
        await loadSessionData(chatStore.currentSessionId)
      }
    }
  }
})

// 组件激活时（切换页面回来），如果面板可见且有选中的客户端，刷新会话列表
onActivated(async () => {
  // ✅ 只在会话列表为空时才刷新，避免覆盖本地会话信息
  if (chatStore.visible && chatStore.selectedClientId && chatStore.sessions.length === 0) {
    await refreshSessions()
    // ✅ 如果刷新会话列表后，有当前选中的会话，自动加载该会话的消息
    if (chatStore.currentSessionId && chatStore.sessions.length > 0) {
      const currentSession = chatStore.sessions.find(s => s.sessionId === chatStore.currentSessionId)
      if (currentSession) {
        console.log('[ChatPanel] 页面激活后自动加载当前会话消息:', chatStore.currentSessionId)
        await loadSessionData(chatStore.currentSessionId)
      }
    }
  }
  // ✅ 如果当前有会话，刷新订单记录
  if (currentSession.value) {
    refreshOrderRecord()
  }
})

// ✅ 监听WebSocket订单状态更新
websocketManager.on((data) => {
  if (data.type === 'order_status_update' || (data.type === 'client_response' && data.event === 'order')) {
    // 如果订单状态更新，刷新当前订单记录
    if (currentSession.value) {
      refreshOrderRecord()
    }
  }
})

// ✅ 监听当前会话变化，自动刷新订单记录
watch(() => currentSession.value?.itemId, (newItemId, oldItemId) => {
  if (newItemId && newItemId !== oldItemId) {
    console.log('[ChatPanel] 会话切换，刷新订单记录: itemId=', newItemId)
    refreshOrderRecord()
  } else if (!newItemId) {
    currentOrderRecord.value = null
  }
}, { immediate: true })
</script>

<style scoped>
.chat-panel {
  position: fixed;
  background: #fff;
  border-radius: 16px;
  box-shadow: 0 18px 40px rgba(15, 37, 67, 0.22);
  z-index: 2000;  /* 提高层级，避免被侧边栏遮挡，对话框会使用更高的 z-index (3000+) */
  display: flex;
  flex-direction: column;
  overflow: hidden;
  user-select: none;
}

.chat-panel__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid #eef1f6;
  background: linear-gradient(135deg, #f7faff 0%, #edf3ff 100%);
  cursor: move;
  user-select: none;
}

.chat-panel__header:hover {
  cursor: move;
}

.chat-panel__header-left {
  display: flex;
  align-items: center;
  gap: 12px;
}

.chat-panel__brand-icon {
  font-size: 20px;
  color: #5a61ff;
}

.chat-panel__brand-text {
  font-weight: 600;
  color: #2a2f4a;
  font-size: 15px;
}

.chat-panel__client-select {
  width: 240px;
}

/* ✅ 关闭按钮样式 - 更大、更容易点击 */
.chat-panel__close-btn {
  padding: 8px 12px !important;
  min-width: 40px;
  min-height: 40px;
  border-radius: 8px;
  transition: all 0.3s ease;
}

.chat-panel__close-btn:hover {
  background-color: rgba(0, 0, 0, 0.06) !important;
  transform: scale(1.1);
}

.chat-panel__close-btn:active {
  transform: scale(0.95);
}

.chat-panel__body {
  flex: 1;
  display: flex;
  background: #f5f8ff;
  min-height: 0; /* 确保flex布局正常工作 */
  overflow: hidden; /* 防止内容溢出 */
}

.chat-panel__placeholder {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

.chat-panel__sidebar {
  width: 220px;
  background: #fff;
  border-right: 1px solid #e7ecf5;
  padding: 12px;
  display: flex;
  flex-direction: column;
}

.chat-panel__sidebar-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-weight: 600;
  color: #2a2f4a;
  margin-bottom: 10px;
}

.chat-panel__sidebar-actions {
  display: flex;
  gap: 6px;
}

/* ✅ 会话搜索框样式 */
.chat-panel__search {
  padding: 0 0 12px 0;
  margin-bottom: 8px;
  border-bottom: 1px solid #e8eaed;
}

/* ✅ 视频播放图标覆盖层 */
.video-play-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: white;
  background: rgba(0, 0, 0, 0.5);
  border-radius: 50%;
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

/* ✅ 缩略图列表（横向滚动，类似京东样式） */
.chat-info__thumbnails {
  display: flex;
  gap: 4px;
  margin-top: 8px;
  overflow-x: auto;
  overflow-y: hidden;
  padding: 2px 0;
  scrollbar-width: thin;
}

.chat-info__thumbnails::-webkit-scrollbar {
  height: 4px;
}

.chat-info__thumbnails::-webkit-scrollbar-track {
  background: #f5f7fa;
  border-radius: 2px;
}

.chat-info__thumbnails::-webkit-scrollbar-thumb {
  background: #dcdfe6;
  border-radius: 2px;
}

.chat-info__thumbnails::-webkit-scrollbar-thumb:hover {
  background: #c0c4cc;
}

.thumbnail-item {
  position: relative;
  width: 50px;
  height: 50px;
  flex-shrink: 0;
  border-radius: 3px;
  overflow: hidden;
  cursor: pointer;
  border: 1.5px solid #e4e7ed;
  transition: all 0.2s;
  background: #f5f7fa;
}

.thumbnail-item :deep(.el-image) {
  width: 100%;
  height: 100%;
}

.thumbnail-item :deep(.el-image__inner) {
  object-fit: cover !important;
  width: 100%;
  height: 100%;
}

.thumbnail-item:hover {
  border-color: #409EFF;
  transform: scale(1.08);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.thumbnail-item.active {
  border-color: #409EFF;
  border-width: 2px;
  box-shadow: 0 0 4px rgba(64, 158, 255, 0.5);
}

.thumbnail-video-badge {
  position: absolute;
  bottom: 2px;
  right: 2px;
  background: rgba(0, 0, 0, 0.75);
  color: white;
  border-radius: 50%;
  width: 16px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
}

/* ✅ 视频播放器样式 */
.video-player-dialog :deep(.el-dialog__body) {
  padding: 0;
  height: 100%;
  background: rgba(0, 0, 0, 0.9);
}

.video-player-fullscreen {
  position: relative;
  width: 100%;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.9);
}

.video-player-close {
  position: absolute;
  top: 20px;
  right: 20px;
  width: 48px;
  height: 48px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 1000;
  color: white;
  transition: all 0.3s;
}

.video-player-close:hover {
  background: rgba(255, 255, 255, 0.2);
  transform: scale(1.1);
}

.video-player-content {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 60px;
}

.chat-session-list {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden; /* ✅ 确保列表不会溢出 */
}

.chat-session-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 8px;
  border-radius: 10px;
  cursor: pointer;
  transition: background 0.2s ease;
}

.chat-session-item:hover {
  background: #f4f7ff;
}

.chat-session-item.active {
  background: rgba(90, 97, 255, 0.1);
}

.chat-session-item__avatar {
  position: relative;
  width: 38px;
  height: 38px;
  flex-shrink: 0;
  /* ✅ 不设置 overflow: hidden，让角标可以显示在头像外面（覆盖在头像上） */
  /* overflow: hidden; */
  background: #f2f3f5;
}

/* ✅ 头像本身的圆形裁剪 */
.chat-session-item__avatar .el-avatar {
  border-radius: 50%;
  overflow: hidden;
}

.chat-session-item__avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.chat-session-item__badge {
  position: absolute;
  top: -2px;
  right: -2px;
  min-width: 18px;
  height: 18px;
  line-height: 18px;
  padding: 0 4px;
  border-radius: 9px;
  background: #f56c6c;
  color: #fff;
  font-size: 11px;
  text-align: center;
  font-weight: 600;
  z-index: 10;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
  white-space: nowrap;
  /* ✅ 确保角标覆盖在头像上方，不被遮挡 */
  pointer-events: none;
  /* ✅ 使用flex布局确保文字居中 */
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ✅ 商品已下架错误标记 */
.chat-session-item__error-badge {
  position: absolute;
  top: -2px;
  right: -2px;
  width: 20px;
  height: 20px;
  line-height: 20px;
  border-radius: 10px;
  background: #f56c6c;
  color: #fff;
  font-size: 12px;
  text-align: center;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
  box-shadow: 0 2px 4px rgba(245, 108, 108, 0.4);
  pointer-events: none;
}

/* ✅ 右键菜单 */
.chat-context-menu {
  position: fixed;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
  padding: 4px 0;
  min-width: 140px;
  z-index: 99999;  /* 提高到最高层级，确保在所有元素之上 */
}

.context-menu-item {
  padding: 8px 16px;
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  transition: background 0.2s;
  font-size: 14px;
  color: #606266;
}

.context-menu-item:hover {
  background: #f5f7fa;
}

.context-menu-item--danger {
  color: #f56c6c;
}

.context-menu-item--danger:hover {
  background: #fef0f0;
}

.context-menu-item--success {
  color: #67c23a;
}

.context-menu-item--success:hover {
  background: #f0f9ff;
}

.context-menu-divider {
  height: 1px;
  background: #e4e7ed;
  margin: 4px 0;
}

.chat-session-item__content {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.chat-session-item__title {
  font-size: 13px;
  font-weight: 600;
  color: #2a2f4a;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chat-session-item__message {
  font-size: 12px;
  color: #9aa1b5;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chat-session-item__time {
  font-size: 11px;
  color: #a0a7bd;
}

.chat-session-empty {
  margin-top: 40px;
}

.chat-panel__main {
  flex: 1;
  display: flex;
  flex-direction: column;
  background: #f4f6fb;
  min-width: 0; /* 确保flex布局正常工作 */
  overflow: hidden; /* 防止内容溢出 */
}

.chat-main__header {
  padding: 12px 16px;
  border-bottom: 1px solid #e6ebf5;
  background: #fff;
}

.chat-main__seller-name {
  font-size: 15px;
  font-weight: 600;
  color: #303756;
}

.chat-main__seller-sub {
  margin-top: 4px;
  font-size: 12px;
  color: #9aa1b5;
}

.chat-main__messages {
  flex: 1;
  min-height: 0;
  padding: 16px 20px;
}

.chat-main__no-messages {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  min-height: 200px;
}

.chat-main__loading-more {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px;
  color: #909399;
  font-size: 14px;
  background: #f5f7fa;
  border-radius: 8px;
  margin: 8px 12px;
}

.chat-bubble {
  max-width: 72%;
  margin-bottom: 14px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
}

.chat-bubble.self {
  margin-left: auto;
  align-items: flex-end;
}

.chat-bubble__content {
  background: #fff;
  border-radius: 14px;
  padding: 10px 14px;
  color: #27314d;
  line-height: 1.5;
  box-shadow: 0 6px 18px rgba(122, 132, 186, 0.12);
}

.chat-bubble.self .chat-bubble__content {
  background: #5a61ff;
  color: #fff;
}

.chat-bubble__time {
  font-size: 11px;
  color: #a4aac2;
}

.chat-bubble__status {
  display: inline-flex;
  align-items: center;
  margin-left: 6px;
  font-size: 12px;
}

.chat-bubble__status.error {
  color: #f56c6c;
}

.chat-bubble__status .error-text {
  margin-left: 4px;
  cursor: pointer;
}

/* ✅ 已读状态样式 */
.chat-bubble__read-status {
  display: inline-flex;
  align-items: center;
  margin-left: 6px;
  font-size: 12px;
  color: #67c23a;
}

.chat-bubble__read-status.read {
  color: #67c23a;
}

.read-status-text {
  margin-left: 4px;
  font-size: 11px;
  color: #909399;
}

.chat-bubble.self .read-status-text {
  color: rgba(255, 255, 255, 0.8);
}

.chat-bubble.pending .chat-bubble__content {
  opacity: 0.7;
}

.chat-bubble.failed .chat-bubble__content {
  border: 1px solid #f56c6c;
}

/* ✅ 图片消息样式 */
.chat-message-image {
  margin: 4px 0;
}

/* ✅ 系统消息样式 */
.chat-message-system {
  margin: 8px 0;
}

.system-message-content {
  line-height: 1.6;
  word-wrap: break-word;
}

.system-message-content a {
  color: #409eff;
  text-decoration: underline;
}

.system-message-content a:hover {
  color: #66b1ff;
}

.system-message-content strong {
  font-weight: 600;
}

/* ✅ 语音消息样式 */
.chat-message-voice {
  margin: 4px 0;
}

/* ✅ 视频消息样式 */
.chat-message-video {
  margin: 4px 0;
  position: relative;
}

.chat-message-video video {
  display: block;
  max-width: 300px;
  max-height: 400px;
  border-radius: 8px;
  background: #000;
}

.chat-message-video .video-duration {
  position: absolute;
  bottom: 8px;
  right: 8px;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  padding: 2px 6px;
  border-radius: 4px;
  font-size: 12px;
  pointer-events: none;
}

.voice-player {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  background: #f5f7fa;
  border-radius: 18px;
  cursor: pointer;
  user-select: none;
  transition: background 0.2s;
  min-width: 120px;
}

.voice-player:hover {
  background: #e9ecef;
}

.voice-icon {
  font-size: 18px;
  color: #409eff;
  flex-shrink: 0;
}

.voice-icon.playing {
  color: #f56c6c;
  animation: pulse 1s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% {
    opacity: 1;
  }
  50% {
    opacity: 0.6;
  }
}

.voice-wave {
  display: flex;
  align-items: center;
  gap: 2px;
  flex: 1;
  height: 20px;
}

.voice-wave-bar {
  width: 2px;
  height: 8px;
  background: #c0c4cc;
  border-radius: 1px;
  transition: all 0.3s ease;
}

.voice-wave-bar.active {
  background: #409eff;
  animation: wave 0.8s ease-in-out infinite;
}

.voice-wave-bar:nth-child(1) { animation-delay: 0s; }
.voice-wave-bar:nth-child(2) { animation-delay: 0.05s; }
.voice-wave-bar:nth-child(3) { animation-delay: 0.1s; }
.voice-wave-bar:nth-child(4) { animation-delay: 0.15s; }
.voice-wave-bar:nth-child(5) { animation-delay: 0.2s; }
.voice-wave-bar:nth-child(6) { animation-delay: 0.25s; }
.voice-wave-bar:nth-child(7) { animation-delay: 0.3s; }
.voice-wave-bar:nth-child(8) { animation-delay: 0.35s; }
.voice-wave-bar:nth-child(9) { animation-delay: 0.4s; }
.voice-wave-bar:nth-child(10) { animation-delay: 0.45s; }
.voice-wave-bar:nth-child(11) { animation-delay: 0.5s; }
.voice-wave-bar:nth-child(12) { animation-delay: 0.55s; }
.voice-wave-bar:nth-child(13) { animation-delay: 0.6s; }
.voice-wave-bar:nth-child(14) { animation-delay: 0.65s; }
.voice-wave-bar:nth-child(15) { animation-delay: 0.7s; }
.voice-wave-bar:nth-child(16) { animation-delay: 0.75s; }
.voice-wave-bar:nth-child(17) { animation-delay: 0.8s; }
.voice-wave-bar:nth-child(18) { animation-delay: 0.85s; }
.voice-wave-bar:nth-child(19) { animation-delay: 0.9s; }
.voice-wave-bar:nth-child(20) { animation-delay: 0.95s; }

@keyframes wave {
  0%, 100% {
    height: 8px;
  }
  50% {
    height: 16px;
  }
}

.voice-duration {
  font-size: 12px;
  color: #909399;
  flex-shrink: 0;
  min-width: 30px;
  text-align: right;
}

.chat-main__input-wrapper {
  flex-shrink: 0; /* 防止输入区域被压缩 */
  border-top: 1px solid #dde3f2;
  background: #fff;
}

.chat-main__input {
  padding: 12px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.chat-main__input-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 8px;
  flex-shrink: 0; /* 确保按钮区域不被压缩 */
}

.chat-main__empty {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

.chat-panel__info {
  width: 240px;
  min-width: 240px; /* 防止被压缩 */
  background: #fff;
  border-left: 1px solid #e7ecf5;
  padding: 16px;
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden; /* 防止内容溢出 */
}

.chat-info__section {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.chat-info__title {
  font-weight: 600;
  color: #303756;
  margin-bottom: 12px;
  font-size: 14px;
}

.chat-info__content {
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.chat-info__image {
  position: relative; /* ✅ 为水印遮罩提供定位参考 */
  width: 100%;
  margin-bottom: 8px;
}

/* ✅ 商品图片包装器 - 支持遮罩层 */
.chat-info__image-wrapper {
  position: relative;
  width: 100%;
}

/* ✅ 已卖掉遮罩层 */
.sold-out-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.85);
  border-radius: 8px;
  pointer-events: none;
  z-index: 10;
}

/* ✅ 已卖掉徽章 - 印章风格 */
.sold-out-badge {
  position: relative;
  width: 120px;
  height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  transform: rotate(-15deg);
}

/* ✅ 印章圆形边框（外圈） */
.sold-out-badge::before {
  content: '';
  position: absolute;
  width: 100%;
  height: 100%;
  border: 4px solid rgba(128, 128, 128, 0.6);
  border-radius: 50%;
  box-sizing: border-box;
}

/* ✅ 印章圆形边框（内圈） */
.sold-out-badge::after {
  content: '';
  position: absolute;
  width: 85%;
  height: 85%;
  border: 2px solid rgba(128, 128, 128, 0.5);
  border-radius: 50%;
  box-sizing: border-box;
}

/* ✅ 水印内容容器 */
.sold-out-content {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}

/* ✅ 主标题文字 */
.sold-out-text-main {
  font-size: 26px;
  font-weight: 900;
  color: rgba(128, 128, 128, 0.7);
  letter-spacing: 4px;
  font-family: "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
}

/* ✅ 副标题文字 */
.sold-out-text-sub {
  font-size: 10px;
  font-weight: 600;
  color: rgba(128, 128, 128, 0.6);
  letter-spacing: 1px;
  font-family: Arial, sans-serif;
}

/* ✅ 装饰线条容器 */
.sold-out-lines {
  position: absolute;
  right: -10px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  gap: 3px;
  z-index: 1;
}

/* ✅ 装饰线条 */
.sold-out-lines .line {
  width: 25px;
  height: 2px;
  background: rgba(128, 128, 128, 0.5);
  border-radius: 1px;
}

.sold-out-lines .line:nth-child(2),
.sold-out-lines .line:nth-child(3) {
  width: 30px;
}

.chat-info__title-section {
  margin-bottom: 8px;
}

.chat-info__item-title {
  font-size: 13px;
  color: #303756;
  line-height: 1.5;
  word-break: break-word;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  transition: color 0.3s ease, transform 0.2s ease;
}

/* ✅ 商品标题可点击样式 */
.chat-info__item-title.clickable {
  cursor: pointer;
}

.chat-info__item-title.clickable:hover {
  color: #1890ff;
  text-decoration: underline;
  transform: translateY(-1px);
}

.chat-info__item-title.clickable:active {
  transform: translateY(0);
}

.chat-info__price-section {
  margin-bottom: 8px;
}

.chat-info__item {
  font-size: 12px;
  margin-bottom: 8px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.chat-info__title-text {
  word-break: break-word;
  max-width: 200px;
  display: block;
}

.chat-info__price {
  color: #f56c6c;
  font-weight: 600;
  font-size: 18px;
}

.chat-info__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-bottom: 8px;
}

.chat-info__actions {
  margin-top: auto;
  padding-top: 16px;
  border-top: 1px solid #e7ecf5;
  flex-shrink: 0; /* 确保按钮区域不被压缩 */
}

.chat-panel__resize-handle {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 20px;
  height: 20px;
  cursor: nwse-resize;
  background: linear-gradient(135deg, transparent 0%, transparent 30%, #ccc 30%, #ccc 35%, transparent 35%, transparent 65%, #ccc 65%, #ccc 70%, transparent 70%);
  z-index: 10;
  /* ✅ 确保调整大小手柄在面板最上层，不会被其他元素遮挡 */
  pointer-events: auto;
}

.chat-panel__resize-handle:hover {
  background: linear-gradient(135deg, transparent 0%, transparent 30%, #5a61ff 30%, #5a61ff 35%, transparent 35%, transparent 65%, #5a61ff 65%, #5a61ff 70%, transparent 70%);
}

/* ✅ 添加右侧宽度调整手柄（仅调整宽度） */
.chat-panel__resize-handle-vertical {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 20px; /* 避免与右下角手柄重叠 */
  width: 4px;
  cursor: ew-resize; /* 左右调整光标 */
  z-index: 10;
  pointer-events: auto;
}

.chat-panel__resize-handle-vertical:hover {
  background: rgba(90, 97, 255, 0.3);
}

/* ✅ 添加底部高度调整手柄（仅调整高度） */
.chat-panel__resize-handle-horizontal {
  position: absolute;
  left: 0;
  right: 20px; /* 避免与右下角手柄重叠 */
  bottom: 0;
  height: 4px;
  cursor: ns-resize; /* 上下调整光标 */
  z-index: 10;
  pointer-events: auto;
}

.chat-panel__resize-handle-horizontal:hover {
  background: rgba(90, 97, 255, 0.3);
}

.chat-info__item .label {
  color: #9aa1b5;
}

.chat-info__item .value {
  color: #27314d;
  word-break: break-all;
}

.chat-panel-slide-enter-active,
.chat-panel-slide-leave-active {
  transition: all 0.2s ease;
}

.chat-panel-slide-enter-from,
.chat-panel-slide-leave-to {
  opacity: 0;
  transform: translateY(16px);
}

/* ✅ 输入框工具栏 */
.chat-input-toolbar {
  display: flex;
  gap: 8px;
  margin-bottom: 8px;
}

/* ✅ 表情包选择器 */
.emoji-picker {
  position: absolute;
  bottom: 100%;
  left: 0;
  width: 320px;
  max-height: 300px;
  background: #fff;
  border: 1px solid #e4e7ed;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  z-index: 1000;
  margin-bottom: 8px;
}

.emoji-picker__header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 12px;
  border-bottom: 1px solid #e4e7ed;
  font-size: 14px;
  font-weight: 500;
}

.emoji-picker__content {
  padding: 8px;
  max-height: 250px;
  overflow-y: auto;
}

.emoji-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 4px;
}

.emoji-item {
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border-radius: 4px;
  transition: background-color 0.2s;
}

.emoji-item:hover {
  background-color: #f5f7fa;
}

.emoji-item img {
  width: 32px;
  height: 32px;
  object-fit: contain;
}

.emoji-empty {
  padding: 20px;
  text-align: center;
}
</style>

